Merge Mainline

This commit is contained in:
CN_SZTL 2020-12-26 23:06:26 +08:00
commit d6d3518a46
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
145 changed files with 2088 additions and 5216 deletions

View File

@ -14,7 +14,7 @@ PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/lrinQVQ/openwrt-chinadns/releases/download/v$(PKG_VERSION)
PKG_MD5SUM:=5c7aa6a89a969a1dd6469b4808b0759b
PKG_HASH:=caab37a27c444d917f0b92c65d7082543dc22acc7c24322be07a108a44794369
PKG_LICENSE:=GPLv3
PKG_LICENSE_FILES:=LICENSE

View File

@ -11,6 +11,7 @@ PKG_VERSION:=1.2.2
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/cbeuw/GoQuiet.git
PKG_MIRROR_HASH:=d16557d89ac909877128729590e34a3c7a9b430000525ed133e2fffb56a0310a
PKG_SOURCE_VERSION:=013cdfdf72000dcd4691799c37a0cf960ab4c82f
PKG_SOURCE_SUBDIR:=$(PKG_NAME)

View File

@ -13,6 +13,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_DATE:=2020-12-15
PKG_SOURCE_URL:=https://github.com/iawia002/annie.git
PKG_MIRROR_HASH:=7ee175f0dec4ca2efbaaca746f33ab3f138d70c76eca1f87d1e3950478f4e110
PKG_SOURCE_VERSION:=abc3c9df18173c91a5ca7a77fecc0665dea01aa1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION)

View File

@ -13,7 +13,7 @@ PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=@SF/cups-bjnp
PKG_MD5SUM:=8a337091b65abfdd70191129efefb9b3
PKG_HASH:=2acb716680d66f1378cf8dcd45fedf7f72ccc9b66fa80214d6dcb042b5e3c9ab
PKG_BUILD_DEPENDS:=cups

View File

@ -14,7 +14,7 @@ PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-source.tar.gz
PKG_SOURCE_URL:=http://www.cups.org/software/$(PKG_VERSION)
PKG_MD5SUM:=9f9bf6e3b9c20a3519b4dc409666d6e7
PKG_HASH:=4b14fd833180ac529ebebea766a09094c2568bf8426e219cb3a1715304ef728d
TARGET_LDFLAGS+=-Wl,-rpath-link=$(STAGING_DIR)/usr/lib

View File

@ -12,6 +12,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/svaarala/duktape.git
PKG_MIRROR_HASH:=7e9caaf9c7e82bc01f859019359b3a13fa038f0e5e82a6541687a30c1ccfa604
PKG_SOURCE_VERSION:=19cc8f5bb855791ff55cbf60d2cea72df485e86f
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz

View File

@ -6,6 +6,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/ginuerzh/gost.git
PKG_MIRROR_HASH:=dfe0d9b548a71b1d476cecf434cce094594ee46a7b0563284c902bec4fc01a3f
PKG_SOURCE_VERSION:=2707a8f0a90e111fc009791a6a911405939a25fb
PKG_MAINTAINER:=[CTCGFW] Project OpenWrt

View File

@ -6,6 +6,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/xxxserxxx/gotop.git
PKG_MIRROR_HASH:=b4de9c3fdd144f57dfd713471c9ac7e99ff82b110e1dc0d7ecf1e3d0aaf5058d
PKG_SOURCE_VERSION:=a8a238ac18725e377addae8a23bf08cf476b404c
PKG_MAINTAINER:=[CTCGFW] Project OpenWrt

View File

@ -6,6 +6,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/1715173329/gowebdav.git
PKG_MIRROR_HASH:=6917c641ede5edc31de16b38686c8d42b1d327fe99d744972eafc544750c4c0f
PKG_SOURCE_VERSION:=8b30e5453e959fd9911a90f2d4f7421616285ffa
PKG_MAINTAINER:=CN_SZTL <cnsztl@project-openwrt.eu.org>

View File

@ -14,7 +14,7 @@ PKG_MAINTAINER:=fabled
PKG_LICENSE:=MIT
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_MIRROR_HASH:=f9b870968f75c8020a311cca2482139ac8b2b55d4f1b5b0ce00f7844b083c866
PKG_MIRROR_HASH:=8aff0958546e4082d48854fdeb32f4fd1ef6a9f78ef066a1886f4d9569898d0e
PKG_SOURCE_URL:=https://github.com/fabled/lua-maxminddb.git
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=93da9f4e6c814c3a23044dd2cdd22d4a6b4f665b

View File

@ -1,13 +1,14 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-diskman
PKG_VERSION:=v0.2.10
PKG_VERSION:=v0.2.11
PKG_RELEASE:=beta
PKG_MAINTAINER:=lisaac <https://github.com/lisaac/luci-app-diskman>
PKG_LICENSE:=AGPL-3.0
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/lisaac/luci-app-diskman.git
PKG_MIRROR_HASH:=b5ece7cb3918b9ca7ea0aabbf2e7b637ebf2b71ea2654117065c57079fad7df3
PKG_SOURCE_VERSION:=$(PKG_VERSION)
PKG_SOURCE_SUBDIR:=$(PKG_NAME)

View File

@ -1 +0,0 @@
e2fsprogs parted smartmontools blkid mdadm btrfs-progs util-linux

View File

@ -1,155 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-diskman>
]]--
require "luci.util"
module("luci.controller.diskman",package.seeall)
function index()
-- check all used executables in disk management are existed
local CMD = {"parted", "blkid", "smartctl"}
local executables_all_existed = true
for _, cmd in ipairs(CMD) do
local command = luci.sys.exec("/usr/bin/which " .. cmd)
if not command:match(cmd) then
executables_all_existed = false
break
end
end
if not executables_all_existed then return end
-- entry(path, target, title, order)
-- set leaf attr to true to pass argument throughe url (e.g. admin/system/disk/partition/sda)
entry({"admin", "system", "diskman"}, alias("admin", "system", "diskman", "disks"), _("Disk Man"), 55)
entry({"admin", "system", "diskman", "disks"}, form("diskman/disks"), nil).leaf = true
entry({"admin", "system", "diskman", "partition"}, form("diskman/partition"), nil).leaf = true
entry({"admin", "system", "diskman", "btrfs"}, form("diskman/btrfs"), nil).leaf = true
entry({"admin", "system", "diskman", "format_partition"}, call("format_partition"), nil).leaf = true
entry({"admin", "system", "diskman", "get_disk_info"}, call("get_disk_info"), nil).leaf = true
entry({"admin", "system", "diskman", "mk_p_table"}, call("mk_p_table"), nil).leaf = true
entry({"admin", "system", "diskman", "smartdetail"}, call("smart_detail"), nil).leaf = true
entry({"admin", "system", "diskman", "smartattr"}, call("smart_attr"), nil).leaf = true
end
function format_partition()
local partation_name = luci.http.formvalue("partation_name")
local fs = luci.http.formvalue("file_system")
if not partation_name then
luci.http.status(500, "Partition NOT found!")
luci.http.write_json("Partition NOT found!")
return
elseif not nixio.fs.access("/dev/"..partation_name) then
luci.http.status(500, "Partition NOT found!")
luci.http.write_json("Partition NOT found!")
return
elseif not fs then
luci.http.status(500, "no file system")
luci.http.write_json("no file system")
return
end
local dm = require "luci.model.diskman"
code, msg = dm.format_partition(partation_name, fs)
luci.http.status(code, msg)
luci.http.write_json(msg)
end
function get_disk_info(dev)
if not dev then
luci.http.status(500, "no device")
luci.http.write_json("no device")
return
elseif not nixio.fs.access("/dev/"..dev) then
luci.http.status(500, "no device")
luci.http.write_json("no device")
return
end
local dm = require "luci.model.diskman"
local device_info = dm.get_disk_info(dev)
luci.http.status(200, "ok")
luci.http.prepare_content("application/json")
luci.http.write_json(device_info)
end
function mk_p_table()
local p_table = luci.http.formvalue("p_table")
local dev = luci.http.formvalue("dev")
if not dev then
luci.http.status(500, "no device")
luci.http.write_json("no device")
return
elseif not nixio.fs.access("/dev/"..dev) then
luci.http.status(500, "no device")
luci.http.write_json("no device")
return
end
local dm = require "luci.model.diskman"
if p_table == "GPT" or p_table == "MBR" then
p_table = p_table == "MBR" and "msdos" or "gpt"
local res = luci.sys.call(dm.command.parted .. " -s /dev/" .. dev .. " mktable ".. p_table)
if res == 0 then
luci.http.status(200, "ok")
else
luci.http.status(500, "command exec error")
end
luci.http.prepare_content("application/json")
luci.http.write_json({code=res})
else
luci.http.status(404, "not support")
luci.http.prepare_content("application/json")
luci.http.write_json({code="1"})
end
end
function smart_detail(dev)
luci.template.render("diskman/smart_detail", {dev=dev})
end
function smart_attr(dev)
local dm = require "luci.model.diskman"
local cmd = io.popen(dm.command.smartctl .. " --attributes /dev/%s" % dev)
if cmd then
local attr = { }
if dev:match("nvme")then
while true do
local ln = cmd:read("*l")
if not ln then
break
elseif ln:match("^(.-):%s+(.+)") then
local key, value = ln:match("^(.-):%s+(.+)")
attr[#attr+1]= {
key = key,
value = value
}
end
end
else
while true do
local ln = cmd:read("*l")
if not ln then
break
elseif ln:match("^.*%d+%s+.+%s+.+%s+.+%s+.+%s+.+%s+.+%s+.+%s+.+%s+.+") then
local id,attrbute,flag,value,worst,thresh,type,updated,raw = ln:match("^%s*(%d+)%s+([%a%p]+)%s+(%w+)%s+(%d+)%s+(%d+)%s+(%d+)%s+([%a%p]+)%s+(%a+)%s+[%w%p]+%s+(.+)")
id= "%x" % id
if not id:match("^%w%w") then
id = "0%s" % id
end
attr[#attr+1]= {
id = id:upper(),
attrbute = attrbute,
flag = flag,
value = value,
worst = worst,
thresh = thresh,
type = type,
updated = updated,
raw = raw
}
end
end
end
cmd:close()
luci.http.prepare_content("application/json")
luci.http.write_json(attr)
end
end

View File

@ -1,210 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-diskman>
]]--
require "luci.util"
require("luci.tools.webadmin")
local dm = require "luci.model.diskman"
local uuid = arg[1]
if not uuid then luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman")) end
-- mount subv=/ to tempfs
mount_point = "/tmp/.btrfs_tmp"
nixio.fs.mkdirr(mount_point)
luci.util.exec(dm.command.umount .. " "..mount_point .. " >/dev/null 2>&1")
luci.util.exec(dm.command.mount .. " -t btrfs -o subvol=/ UUID="..uuid.." "..mount_point)
m = SimpleForm("btrfs", translate("Btrfs"), translate("Manage Btrfs"))
m.template = "diskman/cbi/xsimpleform"
m.redirect = luci.dispatcher.build_url("admin/system/diskman")
m.submit = false
m.reset = false
-- info
local btrfs_info = dm.get_btrfs_info(mount_point)
local table_btrfs_info = m:section(Table, {btrfs_info}, translate("Btrfs Info"))
table_btrfs_info:option(DummyValue, "uuid", translate("UUID"))
table_btrfs_info:option(DummyValue, "members", translate("Members"))
table_btrfs_info:option(DummyValue, "data_raid_level", translate("Data"))
table_btrfs_info:option(DummyValue, "metadata_raid_lavel", translate("Metadata"))
table_btrfs_info:option(DummyValue, "size_formated", translate("Size"))
table_btrfs_info:option(DummyValue, "used_formated", translate("Used"))
table_btrfs_info:option(DummyValue, "free_formated", translate("Free Space"))
table_btrfs_info:option(DummyValue, "usage", translate("Usage"))
local v_btrfs_label = table_btrfs_info:option(Value, "label", translate("Label"))
local value_btrfs_label = ""
v_btrfs_label.write = function(self, section, value)
value_btrfs_label = value or ""
end
local btn_update_label = table_btrfs_info:option(Button, "_update_label")
btn_update_label.inputtitle = translate("Update")
btn_update_label.inputstyle = "edit"
btn_update_label.write = function(self, section, value)
local cmd = dm.command.btrfs .. " filesystem label " .. mount_point .. " " .. value_btrfs_label
local res = luci.util.exec(cmd)
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
end
-- subvolume
local subvolume_list = dm.get_btrfs_subv(mount_point)
subvolume_list["_"] = { ID = 0 }
table_subvolume = m:section(Table, subvolume_list, translate("SubVolumes"))
table_subvolume:option(DummyValue, "id", translate("ID"))
table_subvolume:option(DummyValue, "top_level", translate("Top Level"))
table_subvolume:option(DummyValue, "uuid", translate("UUID"))
table_subvolume:option(DummyValue, "otime", translate("Otime"))
table_subvolume:option(DummyValue, "snapshots", translate("Snapshots"))
local v_path = table_subvolume:option(Value, "path", translate("Path"))
v_path.forcewrite = true
v_path.render = function(self, section, scope)
if subvolume_list[section].ID == 0 then
self.template = "cbi/value"
self.placeholder = "/my_subvolume"
self.forcewrite = true
Value.render(self, section, scope)
else
self.template = "cbi/dvalue"
DummyValue.render(self, section, scope)
end
end
local value_path
v_path.write = function(self, section, value)
value_path = value
end
local btn_set_default = table_subvolume:option(Button, "_subv_set_default", translate("Set Default"))
btn_set_default.forcewrite = true
btn_set_default.inputstyle = "edit"
btn_set_default.template = "diskman/cbi/disabled_button"
btn_set_default.render = function(self, section, scope)
if subvolume_list[section].default_subvolume then
self.view_disabled = true
self.inputtitle = translate("Set Default")
elseif subvolume_list[section].ID == 0 then
self.template = "cbi/dvalue"
else
self.inputtitle = translate("Set Default")
self.view_disabled = false
end
Button.render(self, section, scope)
end
btn_set_default.write = function(self, section, value)
local cmd
if value == translate("Set Default") then
cmd = dm.command.btrfs .. " subvolume set-default " .. mount_point..subvolume_list[section].path
else
cmd = dm.command.btrfs .. " subvolume set-default " .. mount_point.."/"
end
local res = luci.util.exec(cmd.. " 2>&1")
if res and (res:match("ERR") or res:match("not enough arguments")) then
m.errmessage = res
else
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
end
end
local btn_remove = table_subvolume:option(Button, "_subv_remove")
btn_remove.template = "diskman/cbi/disabled_button"
btn_remove.forcewrite = true
btn_remove.render = function(self, section, scope)
if subvolume_list[section].ID == 0 then
btn_remove.inputtitle = translate("Create")
btn_remove.inputstyle = "add"
self.view_disabled = false
elseif subvolume_list[section].path == "/" or subvolume_list[section].default_subvolume then
btn_remove.inputtitle = translate("Delete")
btn_remove.inputstyle = "remove"
self.view_disabled = true
else
btn_remove.inputtitle = translate("Delete")
btn_remove.inputstyle = "remove"
self.view_disabled = false
end
Button.render(self, section, scope)
end
btn_remove.write = function(self, section, value)
local cmd
if value == translate("Delete") then
cmd = dm.command.btrfs .. " subvolume delete " .. mount_point .. subvolume_list[section].path
elseif value == translate("Create") then
if value_path and value_path:match("^/") then
cmd = dm.command.btrfs .. " subvolume create " .. mount_point .. value_path
else
m.errmessage = translate("Please input Subvolume Path, Subvolume must start with '/'")
return
end
end
local res = luci.util.exec(cmd.. " 2>&1")
if res and (res:match("ERR") or res:match("not enough arguments")) then
m.errmessage = luci.util.pcdata(res)
else
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
end
end
-- snapshot
-- local snapshot_list = dm.get_btrfs_subv(mount_point, 1)
-- table_snapshot = m:section(Table, snapshot_list, translate("Snapshots"))
-- table_snapshot:option(DummyValue, "id", translate("ID"))
-- table_snapshot:option(DummyValue, "top_level", translate("Top Level"))
-- table_snapshot:option(DummyValue, "uuid", translate("UUID"))
-- table_snapshot:option(DummyValue, "otime", translate("Otime"))
-- table_snapshot:option(DummyValue, "path", translate("Path"))
-- local snp_remove = table_snapshot:option(Button, "_snp_remove")
-- snp_remove.inputtitle = translate("Delete")
-- snp_remove.inputstyle = "remove"
-- snp_remove.write = function(self, section, value)
-- local cmd = dm.command.btrfs .. " subvolume delete " .. mount_point .. snapshot_list[section].path
-- local res = luci.util.exec(cmd.. " 2>&1")
-- if res and (res:match("ERR") or res:match("not enough arguments")) then
-- m.errmessage = luci.util.pcdata(res)
-- else
-- luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
-- end
-- end
-- new snapshots
local s_snapshot = m:section(SimpleSection, translate("New Snapshot"))
local value_sorce, value_dest, value_readonly
local v_sorce = s_snapshot:option(Value, "_source", translate("Source Path"), translate("The source path for create the snapshot"))
v_sorce.placeholder = "/data"
v_sorce.forcewrite = true
v_sorce.write = function(self, section, value)
value_sorce = value
end
local v_readonly = s_snapshot:option(Flag, "_readonly", translate("Readonly"), translate("The path where you want to store the snapshot"))
v_readonly.forcewrite = true
v_readonly.rmempty = false
v_readonly.disabled = 0
v_readonly.enabled = 1
v_readonly.default = 1
v_readonly.write = function(self, section, value)
value_readonly = value
end
local v_dest = s_snapshot:option(Value, "_dest", translate("Destination Path (optional)"))
v_dest.forcewrite = true
v_dest.placeholder = "/.snapshot/202002051538"
v_dest.write = function(self, section, value)
value_dest = value
end
local btn_snp_create = s_snapshot:option(Button, "_snp_create")
btn_snp_create.title = " "
btn_snp_create.inputtitle = translate("New Snapshot")
btn_snp_create.inputstyle = "add"
btn_snp_create.write = function(self, section, value)
if value_sorce and value_sorce:match("^/") then
if not value_dest then value_dest = "/.snapshot"..value_sorce.."/"..os.date("%Y%m%d%H%M%S") end
nixio.fs.mkdirr(mount_point..value_dest:match("(.-)[^/]+$"))
local cmd = dm.command.btrfs .. " subvolume snapshot" .. (value_readonly == 1 and " -r " or " ") .. mount_point..value_sorce .. " " .. mount_point..value_dest
local res = luci.util.exec(cmd .. " 2>&1")
if res and (res:match("ERR") or res:match("not enough arguments")) then
m.errmessage = luci.util.pcdata(res)
else
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
end
else
m.errmessage = translate("Please input Source Path of snapshot, Source Path must start with '/'")
end
end
return m

View File

@ -1,327 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-diskman>
]]--
require "luci.util"
require("luci.tools.webadmin")
local dm = require "luci.model.diskman"
-- Use (non-UCI) SimpleForm since we have no related config file
m = SimpleForm("diskman", translate("DiskMan"), translate("Manage Disks over LuCI."))
m.template = "diskman/cbi/xsimpleform"
m:append(Template("diskman/disk_info"))
-- disable submit and reset button
m.submit = false
m.reset = false
-- rescan disks
rescan = m:section(SimpleSection)
rescan_button = rescan:option(Button, "_rescan")
rescan_button.inputtitle= translate("Rescan Disks")
rescan_button.template = "diskman/cbi/inlinebutton"
rescan_button.inputstyle = "add"
rescan_button.forcewrite = true
rescan_button.write = function(self, section, value)
luci.util.exec("echo '- - -' | tee /sys/class/scsi_host/host*/scan > /dev/null")
if dm.command.mdadm then
luci.util.exec(dm.command.mdadm .. " --assemble --scan")
end
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
end
-- disks
local disks = dm.list_devices()
d = m:section(Table, disks, translate("Disks"))
d.config = "disk"
-- option(type, id(key of table), text)
d:option(DummyValue, "path", translate("Path"))
d:option(DummyValue, "model", translate("Model"))
d:option(DummyValue, "sn", translate("Serial Number"))
d:option(DummyValue, "size_formated", translate("Size"))
d:option(DummyValue, "temp", translate("Temp"))
-- d:option(DummyValue, "sec_size", translate("Sector Size "))
d:option(DummyValue, "p_table", translate("Partition Table"))
d:option(DummyValue, "sata_ver", translate("SATA Version"))
-- d:option(DummyValue, "rota_rate", translate("Rotation Rate"))
d:option(DummyValue, "health", translate("Health"))
d:option(DummyValue, "status", translate("Status"))
d.extedit = luci.dispatcher.build_url("admin/system/diskman/partition/%s")
-- raid devices
if dm.command.mdadm then
local raid_devices = dm.list_raid_devices()
-- raid_devices = diskmanager.getRAIDdevices()
if next(raid_devices) ~= nil then
local r = m:section(Table, raid_devices, translate("RAID Devices"))
r.config = "_raid"
r:option(DummyValue, "path", translate("Path"))
r:option(DummyValue, "level", translate("RAID mode"))
r:option(DummyValue, "size_formated", translate("Size"))
r:option(DummyValue, "p_table", translate("Partition Table"))
r:option(DummyValue, "status", translate("Status"))
r:option(DummyValue, "members_str", translate("Members"))
r:option(DummyValue, "active", translate("Active"))
r.extedit = luci.dispatcher.build_url("admin/system/diskman/partition/%s")
end
end
-- btrfs devices
if dm.command.btrfs then
btrfs_devices = dm.list_btrfs_devices()
if next(btrfs_devices) ~= nil then
local table_btrfs = m:section(Table, btrfs_devices, translate("Btrfs"))
table_btrfs:option(DummyValue, "uuid", translate("UUID"))
table_btrfs:option(DummyValue, "label", translate("Label"))
table_btrfs:option(DummyValue, "members", translate("Members"))
-- sieze is error, since there is RAID
-- table_btrfs:option(DummyValue, "size_formated", translate("Size"))
table_btrfs:option(DummyValue, "used_formated", translate("Usage"))
table_btrfs.extedit = luci.dispatcher.build_url("admin/system/diskman/btrfs/%s")
end
end
-- mount point
local mount_point = dm.get_mount_points()
local _mount_point = {}
table.insert( mount_point, { device = 0 } )
local table_mp = m:section(Table, mount_point, translate("Mount Point"))
local v_device = table_mp:option(Value, "device", translate("Device"))
v_device.render = function(self, section, scope)
if mount_point[section].device == 0 then
self.template = "cbi/value"
self.forcewrite = true
for dev, info in pairs(disks) do
for i, v in ipairs(info.partitions) do
self:value("/dev/".. v.name, "/dev/".. v.name .. " ".. v.size_formated)
end
end
Value.render(self, section, scope)
else
self.template = "cbi/dvalue"
DummyValue.render(self, section, scope)
end
end
v_device.write = function(self, section, value)
_mount_point.device = value and value:gsub("%s+", "") or ""
end
local v_fs = table_mp:option(Value, "fs", translate("File System"))
v_fs.render = function(self, section, scope)
if mount_point[section].device == 0 then
self.template = "cbi/value"
self:value("auto", "auto")
self.default = "auto"
self.forcewrite = true
Value.render(self, section, scope)
else
self.template = "cbi/dvalue"
DummyValue.render(self, section, scope)
end
end
v_fs.write = function(self, section, value)
_mount_point.fs = value and value:gsub("%s+", "") or ""
end
local v_mount_option = table_mp:option(Value, "mount_options", translate("Mount Options"))
v_mount_option.render = function(self, section, scope)
if mount_point[section].device == 0 then
self.template = "cbi/value"
self.placeholder = "rw,noauto"
self.forcewrite = true
Value.render(self, section, scope)
else
self.template = "cbi/dvalue"
local mp = mount_point[section].mount_options
mount_point[section].mount_options = nil
local length = 0
for k in mp:gmatch("([^,]+)") do
mount_point[section].mount_options = mount_point[section].mount_options and (mount_point[section].mount_options .. ",") or ""
if length > 20 then
mount_point[section].mount_options = mount_point[section].mount_options.. " <br>"
length = 0
end
mount_point[section].mount_options = mount_point[section].mount_options .. k
length = length + #k
end
self.rawhtml = true
-- mount_point[section].mount_options = #mount_point[section].mount_options > 50 and mount_point[section].mount_options:sub(1,50) .. "..." or mount_point[section].mount_options
DummyValue.render(self, section, scope)
end
end
v_mount_option.write = function(self, section, value)
_mount_point.mount_options = value and value:gsub("%s+", "") or ""
end
local v_mount_point = table_mp:option(Value, "mount_point", translate("Mount Point"))
v_mount_point.render = function(self, section, scope)
if mount_point[section].device == 0 then
self.template = "cbi/value"
self.placeholder = "/media/diskX"
self.forcewrite = true
Value.render(self, section, scope)
else
self.template = "cbi/dvalue"
local new_mp = ""
local v_mp_d
for v_mp_d in self["section"]["data"][section]["mount_point"]:gmatch('[^/]+') do
if #v_mp_d > 12 then
new_mp = new_mp .. "/" .. v_mp_d:sub(1,7) .. ".." .. v_mp_d:sub(-4)
else
new_mp = new_mp .."/".. v_mp_d
end
end
self["section"]["data"][section]["mount_point"] = '<span title="'..self["section"]["data"][section]["mount_point"] .. '" >'..new_mp..'</span>'
self.rawhtml = true
DummyValue.render(self, section, scope)
end
end
v_mount_point.write = function(self, section, value)
_mount_point.mount_point = value
end
local btn_umount = table_mp:option(Button, "_mount", translate("Mount"))
btn_umount.forcewrite = true
btn_umount.render = function(self, section, scope)
if mount_point[section].device == 0 then
self.inputtitle = translate("Mount")
btn_umount.inputstyle = "add"
else
self.inputtitle = translate("Umount")
btn_umount.inputstyle = "remove"
end
Button.render(self, section, scope)
end
btn_umount.write = function(self, section, value)
local res
if value == translate("Mount") then
if not _mount_point.mount_point or not _mount_point.device then return end
luci.util.exec("mkdir -p ".. _mount_point.mount_point)
res = luci.util.exec(dm.command.mount .. " ".. _mount_point.device .. (_mount_point.fs and (" -t ".. _mount_point.fs )or "") .. (_mount_point.mount_options and (" -o " .. _mount_point.mount_options.. " ") or " ").._mount_point.mount_point .. " 2>&1")
elseif value == translate("Umount") then
res = luci.util.exec(dm.command.umount .. " "..mount_point[section].mount_point .. " 2>&1")
end
if res:match("^mount:") or res:match("^umount:") then
m.errmessage = luci.util.pcdata(res)
else
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
end
end
if dm.command.mdadm or dm.command.btrfs then
local creation_section = m:section(TypedSection, "_creation")
creation_section.cfgsections=function()
return {translate("Creation")}
end
creation_section:tab("raid", translate("RAID"), translate("RAID Creation"))
creation_section:tab("btrfs", translate("Btrfs"), translate("Multiple Devices Btrfs Creation"))
-- raid functions
if dm.command.mdadm then
local rname, rmembers, rlevel
local r_name = creation_section:taboption("raid", Value, "_rname", translate("Raid Name"))
r_name.placeholder = "/dev/md0"
r_name.write = function(self, section, value)
rname = value
end
local r_level = creation_section:taboption("raid", ListValue, "_rlevel", translate("Raid Level"))
local valid_raid = luci.util.exec("lsmod | grep md_mod")
if valid_raid:match("linear") then
r_level:value("linear", "Linear")
end
if valid_raid:match("raid456") then
r_level:value("5", "Raid 5")
r_level:value("6", "Raid 6")
end
if valid_raid:match("raid1") then
r_level:value("1", "Raid 1")
end
if valid_raid:match("raid0") then
r_level:value("0", "Raid 0")
end
if valid_raid:match("raid10") then
r_level:value("10", "Raid 10")
end
r_level.write = function(self, section, value)
rlevel = value
end
local r_member = creation_section:taboption("raid", DynamicList, "_rmember", translate("Raid Member"))
for dev, info in pairs(disks) do
if not info.inuse and #info.partitions == 0 then
r_member:value(info.path, info.path.. " ".. info.size_formated)
end
for i, v in ipairs(info.partitions) do
if not v.inuse then
r_member:value("/dev/".. v.name, "/dev/".. v.name .. " ".. v.size_formated)
end
end
end
r_member.write = function(self, section, value)
rmembers = value
end
local r_create = creation_section:taboption("raid", Button, "_rcreate")
r_create.render = function(self, section, scope)
self.title = " "
self.inputtitle = translate("Create Raid")
self.inputstyle = "add"
Button.render(self, section, scope)
end
r_create.write = function(self, section, value)
-- mdadm --create --verbose /dev/md0 --level=stripe --raid-devices=2 /dev/sdb6 /dev/sdc5
local res = dm.create_raid(rname, rlevel, rmembers)
if res and res:match("^ERR") then
m.errmessage = luci.util.pcdata(res)
return
end
dm.gen_mdadm_config()
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
end
end
-- btrfs
if dm.command.btrfs then
local blabel, bmembers, blevel
local btrfs_label = creation_section:taboption("btrfs", Value, "_blabel", translate("Btrfs Label"))
btrfs_label.write = function(self, section, value)
blabel = value
end
local btrfs_level = creation_section:taboption("btrfs", ListValue, "_blevel", translate("Btrfs Raid Level"))
btrfs_level:value("single", "Single")
btrfs_level:value("raid0", "Raid 0")
btrfs_level:value("raid1", "Raid 1")
btrfs_level:value("raid10", "Raid 10")
btrfs_level.write = function(self, section, value)
blevel = value
end
local btrfs_member = creation_section:taboption("btrfs", DynamicList, "_bmember", translate("Btrfs Member"))
for dev, info in pairs(disks) do
if not info.inuse and #info.partitions == 0 then
btrfs_member:value(info.path, info.path.. " ".. info.size_formated)
end
for i, v in ipairs(info.partitions) do
if not v.inuse then
btrfs_member:value("/dev/".. v.name, "/dev/".. v.name .. " ".. v.size_formated)
end
end
end
btrfs_member.write = function(self, section, value)
bmembers = value
end
local btrfs_create = creation_section:taboption("btrfs", Button, "_bcreate")
btrfs_create.render = function(self, section, scope)
self.title = " "
self.inputtitle = translate("Create Btrfs")
self.inputstyle = "add"
Button.render(self, section, scope)
end
btrfs_create.write = function(self, section, value)
-- mkfs.btrfs -L label -d blevel /dev/sda /dev/sdb
local res = dm.create_btrfs(blabel, blevel, bmembers)
if res and res:match("^ERR") then
m.errmessage = luci.util.pcdata(res)
return
end
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
end
end
end
return m

View File

@ -1,366 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-diskman>
]]--
require "luci.util"
require("luci.tools.webadmin")
local dm = require "luci.model.diskman"
local dev = arg[1]
if not dev then
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
elseif not nixio.fs.access("/dev/"..dev) then
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
end
m = SimpleForm("partition", translate("Partition Management"), translate("Partition Disk over LuCI."))
m.template = "diskman/cbi/xsimpleform"
m.redirect = luci.dispatcher.build_url("admin/system/diskman")
m:append(Template("diskman/partition_info"))
-- disable submit and reset button
m.submit = false
m.reset = false
local disk_info = dm.get_disk_info(dev, true)
local format_cmd = dm.get_format_cmd()
s = m:section(Table, {disk_info}, translate("Device Info"))
-- s:option(DummyValue, "key")
-- s:option(DummyValue, "value")
s:option(DummyValue, "path", translate("Path"))
s:option(DummyValue, "model", translate("Model"))
s:option(DummyValue, "sn", translate("Serial Number"))
s:option(DummyValue, "size_formated", translate("Size"))
s:option(DummyValue, "sec_size", translate("Sector Size"))
local dv_p_table = s:option(ListValue, "p_table", translate("Partition Table"))
dv_p_table.render = function(self, section, scope)
-- create table only if not used by raid and no partitions on disk
if not disk_info.p_table:match("Raid") and (#disk_info.partitions == 0 or (#disk_info.partitions == 1 and disk_info.partitions[1].number == -1) or (disk_info.p_table:match("LOOP") and not disk_info.partitions[1].inuse)) then
self:value(disk_info.p_table, disk_info.p_table)
self:value("GPT", "GPT")
self:value("MBR", "MBR")
self.default = disk_info.p_table
ListValue.render(self, section, scope)
else
self.template = "cbi/dvalue"
DummyValue.render(self, section, scope)
end
end
if disk_info.type:match("md") then
s:option(DummyValue, "level", translate("Level"))
s:option(DummyValue, "members_str", translate("Members"))
else
s:option(DummyValue, "temp", translate("Temp"))
s:option(DummyValue, "sata_ver", translate("SATA Version"))
s:option(DummyValue, "rota_rate", translate("Rotation Rate"))
end
s:option(DummyValue, "status", translate("Status"))
local btn_health = s:option(Button, "health", translate("Health"))
btn_health.render = function(self, section, scope)
if disk_info.health then
self.inputtitle = disk_info.health
if disk_info.health == "PASSED" then
self.inputstyle = "add"
else
self.inputstyle = "remove"
end
Button.render(self, section, scope)
else
self.template = "cbi/dvalue"
DummyValue.render(self, section, scope)
end
end
local btn_eject = s:option(Button, "_eject")
btn_eject.template = "diskman/cbi/disabled_button"
btn_eject.inputstyle = "remove"
btn_eject.render = function(self, section, scope)
for i, p in ipairs(disk_info.partitions) do
if p.mount_point ~= "-" then
self.view_disabled = true
break
end
end
if disk_info.p_table:match("Raid") then
self.view_disabled = true
end
if disk_info.type:match("md") then
btn_eject.inputtitle = translate("Remove")
else
btn_eject.inputtitle = translate("Eject")
end
Button.render(self, section, scope)
end
btn_eject.forcewrite = true
btn_eject.write = function(self, section, value)
for i, p in ipairs(disk_info.partitions) do
if p.mount_point ~= "-" then
m.errmessage = p.name .. translate("is in use! please unmount it first!")
return
end
end
if disk_info.type:match("md") then
luci.util.exec(dm.command.mdadm .. " --stop /dev/" .. dev)
luci.util.exec(dm.command.mdadm .. " --remove /dev/" .. dev)
for _, disk in ipairs(disk_info.members) do
luci.util.exec(dm.command.mdadm .. " --zero-superblock " .. disk)
end
dm.gen_mdadm_config()
else
luci.util.exec("echo 1 > /sys/block/" .. dev .. "/device/delete")
end
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
end
-- eject: echo 1 > /sys/block/(device)/device/delete
-- rescan: echo '- - -' | tee /sys/class/scsi_host/host*/scan > /dev/null
-- partitions info
if not disk_info.p_table:match("Raid") then
s_partition_table = m:section(Table, disk_info.partitions, translate("Partitions Info"), translate("Default 2048 sector alignment, support +size{b,k,m,g,t} in End Sector"))
-- s_partition_table:option(DummyValue, "number", translate("Number"))
s_partition_table:option(DummyValue, "name", translate("Name"))
local val_sec_start = s_partition_table:option(Value, "sec_start", translate("Start Sector"))
val_sec_start.render = function(self, section, scope)
-- could create new partition
if disk_info.partitions[section].number == -1 and disk_info.partitions[section].size > 1 * 1024 * 1024 then
self.template = "cbi/value"
Value.render(self, section, scope)
else
self.template = "cbi/dvalue"
DummyValue.render(self, section, scope)
end
end
local val_sec_end = s_partition_table:option(Value, "sec_end", translate("End Sector"))
val_sec_end.render = function(self, section, scope)
-- could create new partition
if disk_info.partitions[section].number == -1 and disk_info.partitions[section].size > 1 * 1024 * 1024 then
self.template = "cbi/value"
Value.render(self, section, scope)
else
self.template = "cbi/dvalue"
DummyValue.render(self, section, scope)
end
end
val_sec_start.forcewrite = true
val_sec_start.write = function(self, section, value)
disk_info.partitions[section]._sec_start = value
end
val_sec_end.forcewrite = true
val_sec_end.write = function(self, section, value)
disk_info.partitions[section]._sec_end = value
end
s_partition_table:option(DummyValue, "size_formated", translate("Size"))
if disk_info.p_table == "MBR" then
s_partition_table:option(DummyValue, "type", translate("Type"))
end
s_partition_table:option(DummyValue, "used_formated", translate("Used"))
s_partition_table:option(DummyValue, "free_formated", translate("Free Space"))
s_partition_table:option(DummyValue, "usage", translate("Usage"))
local dv_mount_point = s_partition_table:option(DummyValue, "mount_point", translate("Mount Point"))
dv_mount_point.rawhtml = true
dv_mount_point.render = function(self, section, scope)
local new_mp = ""
local v_mp_d
for line in self["section"]["data"][section]["mount_point"]:gmatch("[^%s]+") do
if line == '-' then
new_mp = line
break
end
for v_mp_d in line:gmatch('[^/]+') do
if #v_mp_d > 12 then
new_mp = new_mp .. "/" .. v_mp_d:sub(1,7) .. ".." .. v_mp_d:sub(-4)
else
new_mp = new_mp .."/".. v_mp_d
end
end
new_mp = '<span title="'.. line .. '" >' ..new_mp ..'</span>' .. "<br/>"
end
self["section"]["data"][section]["mount_point"] = new_mp
DummyValue.render(self, section, scope)
end
local val_fs = s_partition_table:option(Value, "fs", translate("File System"))
val_fs.forcewrite = true
val_fs.partitions = disk_info.partitions
for k, v in pairs(format_cmd) do
val_fs.format_cmd = val_fs.format_cmd and (val_fs.format_cmd .. "," .. k) or k
end
val_fs.write = function(self, section, value)
disk_info.partitions[section]._fs = value
end
val_fs.render = function(self, section, scope)
-- use listvalue when partition not mounted
if disk_info.partitions[section].mount_point == "-" and disk_info.partitions[section].number ~= -1 and disk_info.partitions[section].type ~= "extended" then
self.template = "diskman/cbi/format_button"
self.inputstyle = "reset"
self.inputtitle = disk_info.partitions[section].fs == "raw" and translate("Format") or disk_info.partitions[section].fs
Button.render(self, section, scope)
-- self:reset_values()
-- self.keylist = {}
-- self.vallist = {}
-- for k, v in pairs(format_cmd) do
-- self:value(k,k)
-- end
-- self.default = disk_info.partitions[section].fs
else
-- self:reset_values()
-- self.keylist = {}
-- self.vallist = {}
self.template = "cbi/dvalue"
DummyValue.render(self, section, scope)
end
end
-- btn_format = s_partition_table:option(Button, "_format")
-- btn_format.template = "diskman/cbi/format_button"
-- btn_format.partitions = disk_info.partitions
-- btn_format.render = function(self, section, scope)
-- if disk_info.partitions[section].mount_point == "-" and disk_info.partitions[section].number ~= -1 and disk_info.partitions[section].type ~= "extended" then
-- self.inputtitle = translate("Format")
-- self.template = "diskman/cbi/disabled_button"
-- self.view_disabled = false
-- self.inputstyle = "reset"
-- for k, v in pairs(format_cmd) do
-- self:depends("val_fs", "k")
-- end
-- -- elseif disk_info.partitions[section].mount_point ~= "-" and disk_info.partitions[section].number ~= -1 then
-- -- self.inputtitle = "Format"
-- -- self.template = "diskman/cbi/disabled_button"
-- -- self.view_disabled = true
-- -- self.inputstyle = "reset"
-- else
-- self.inputtitle = ""
-- self.template = "cbi/dvalue"
-- end
-- Button.render(self, section, scope)
-- end
-- btn_format.forcewrite = true
-- btn_format.write = function(self, section, value)
-- local partition_name = "/dev/".. disk_info.partitions[section].name
-- if not nixio.fs.access(partition_name) then
-- m.errmessage = translate("Partition NOT found!")
-- return
-- end
-- local fs = disk_info.partitions[section]._fs
-- if not format_cmd[fs] then
-- m.errmessage = translate("Filesystem NOT support!")
-- return
-- end
-- local cmd = format_cmd[fs].cmd .. " " .. format_cmd[fs].option .. " " .. partition_name
-- local res = luci.util.exec(cmd .. " 2>&1")
-- if res and res:lower():match("error+") then
-- m.errmessage = luci.util.pcdata(res)
-- else
-- luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/partition/" .. dev))
-- end
-- end
local btn_action = s_partition_table:option(Button, "_action")
btn_action.forcewrite = true
btn_action.template = "diskman/cbi/disabled_button"
btn_action.render = function(self, section, scope)
-- if partition is mounted or the size < 1mb, then disable the add action
if disk_info.partitions[section].mount_point ~= "-" or (disk_info.partitions[section].type ~= "extended" and disk_info.partitions[section].number == -1 and disk_info.partitions[section].size <= 1 * 1024 * 1024) then
self.view_disabled = true
-- self.inputtitle = ""
-- self.template = "cbi/dvalue"
elseif disk_info.partitions[section].type == "extended" and next(disk_info.partitions[section]["logicals"]) ~= nil then
self.view_disabled = true
else
-- self.template = "diskman/cbi/disabled_button"
self.view_disabled = false
end
if disk_info.partitions[section].number ~= -1 then
self.inputtitle = translate("Remove")
self.inputstyle = "remove"
else
self.inputtitle = translate("New")
self.inputstyle = "add"
end
Button.render(self, section, scope)
end
btn_action.write = function(self, section, value)
if value == translate("New") then
local start_sec = disk_info.partitions[section]._sec_start and tonumber(disk_info.partitions[section]._sec_start) or tonumber(disk_info.partitions[section].sec_start)
local end_sec = disk_info.partitions[section]._sec_end
if start_sec then
-- for sector alignment
local align = tonumber(disk_info.phy_sec) / tonumber(disk_info.logic_sec)
align = (align < 2048) and 2048
if start_sec < 2048 then
start_sec = "2048" .. "s"
elseif math.fmod( start_sec, align ) ~= 0 then
start_sec = tostring(start_sec + align - math.fmod( start_sec, align )) .. "s"
else
start_sec = start_sec .. "s"
end
else
m.errmessage = translate("Invalid Start Sector!")
return
end
-- support +size format for End sector
local end_size, end_unit = end_sec:match("^+(%d-)([bkmgtsBKMGTS])$")
if tonumber(end_size) and end_unit then
local unit ={
B=1,
S=512,
K=1024,
M=1048576,
G=1073741824,
T=1099511627776
}
end_unit = end_unit:upper()
end_sec = tostring(tonumber(end_size) * unit[end_unit] / unit["S"] + tonumber(start_sec:sub(1,-2)) - 1 ) .. "s"
elseif tonumber(end_sec) then
end_sec = end_sec .. "s"
else
m.errmessage = translate("Invalid End Sector!")
return
end
local part_type = "primary"
if disk_info.p_table == "MBR" and disk_info["extended_partition_index"] then
if tonumber(disk_info.partitions[disk_info["extended_partition_index"]].sec_start) <= tonumber(start_sec:sub(1,-2)) and tonumber(disk_info.partitions[disk_info["extended_partition_index"]].sec_end) >= tonumber(end_sec:sub(1,-2)) then
part_type = "logical"
if tonumber(start_sec:sub(1,-2)) - tonumber(disk_info.partitions[section].sec_start) < 2048 then
start_sec = tonumber(start_sec:sub(1,-2)) + 2048
start_sec = start_sec .."s"
end
end
elseif disk_info.p_table == "GPT" then
-- AUTOMATIC FIX GPT PARTITION TABLE
-- Not all of the space available to /dev/sdb appears to be used, you can fix the GPT to use all of the space (an extra 16123870 blocks) or continue with the current setting?
local cmd = ' printf "ok\nfix\n" | parted ---pretend-input-tty /dev/'.. dev ..' print'
luci.util.exec(cmd .. " 2>&1")
end
-- partiton
local cmd = dm.command.parted .. " -s -a optimal /dev/" .. dev .. " mkpart " .. part_type .." " .. start_sec .. " " .. end_sec
local res = luci.util.exec(cmd .. " 2>&1")
if res and res:lower():match("error+") then
m.errmessage = luci.util.pcdata(res)
else
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/partition/" .. dev))
end
elseif value == translate("Remove") then
-- remove partition
local number = tostring(disk_info.partitions[section].number)
if (not number) or (number == "") then
m.errmessage = translate("Partition not exists!")
return
end
local cmd = dm.command.parted .. " -s /dev/" .. dev .. " rm " .. number
local res = luci.util.exec(cmd .. " 2>&1")
if res and res:lower():match("error+") then
m.errmessage = luci.util.pcdata(res)
else
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/partition/" .. dev))
end
end
end
end
return m

View File

@ -1,736 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-diskman>
]]--
require "luci.util"
local ver = require "luci.version"
local CMD = {"parted", "mdadm", "blkid", "smartctl", "df", "btrfs", "lsblk"}
local d = {command ={}}
for _, cmd in ipairs(CMD) do
local command = luci.sys.exec("/usr/bin/which " .. cmd)
d.command[cmd] = command:match("^.+"..cmd) or nil
end
d.command.mount = nixio.fs.access("/usr/bin/mount") and "/usr/bin/mount" or "/bin/mount"
d.command.umount = nixio.fs.access("/usr/bin/umount") and "/usr/bin/umount" or "/bin/umount"
local proc_mounts = nixio.fs.readfile("/proc/mounts") or ""
local mounts = luci.util.exec(d.command.mount .. " 2>/dev/null") or ""
local swaps = nixio.fs.readfile("/proc/swaps") or ""
local df = luci.sys.exec(d.command.df .. " 2>/dev/null") or ""
function byte_format(byte)
local suff = {"B", "KB", "MB", "GB", "TB"}
for i=1, 5 do
if byte > 1024 and i < 5 then
byte = byte / 1024
else
return string.format("%.2f %s", byte, suff[i])
end
end
end
local get_smart_info = function(device)
local section
local smart_info = {}
for _, line in ipairs(luci.util.execl(d.command.smartctl .. " -H -A -i -n standby -f brief /dev/" .. device)) do
local attrib, val
if section == 1 then
attrib, val = line:match "^(.-):%s+(.+)"
elseif section == 2 and device:match("nvme") then
attrib, val = line:match("^(.-):%s+(.+)")
if not smart_info.health then smart_info.health = line:match(".-overall%-health.-: (.+)") end
elseif section == 2 then
attrib, val = line:match("^([0-9 ]+)%s+[^ ]+%s+[POSRCK-]+%s+[0-9-]+%s+[0-9-]+%s+[0-9-]+%s+[0-9-]+%s+([0-9-]+)")
if not smart_info.health then smart_info.health = line:match(".-overall%-health.-: (.+)") end
else
attrib = line:match "^=== START OF (.*) SECTION ==="
if attrib and attrib:match("INFORMATION") then
section = 1
elseif attrib and attrib:match("SMART DATA") then
section = 2
elseif not smart_info.status then
val = line:match "^Device is in (.*) mode"
if val then smart_info.status = val end
end
end
if not attrib then
if section ~= 2 then section = 0 end
elseif (attrib == "Power mode is") or
(attrib == "Power mode was") then
smart_info.status = val:match("(%S+)")
-- elseif attrib == "Sector Sizes" then
-- -- 512 bytes logical, 4096 bytes physical
-- smart_info.phy_sec = val:match "([0-9]*) bytes physical"
-- smart_info.logic_sec = val:match "([0-9]*) bytes logical"
-- elseif attrib == "Sector Size" then
-- -- 512 bytes logical/physical
-- smart_info.phy_sec = val:match "([0-9]*)"
-- smart_info.logic_sec = smart_info.phy_sec
elseif attrib == "Serial Number" then
smart_info.sn = val
elseif attrib == "194" or attrib == "Temperature" then
smart_info.temp = val:match("(%d+)") .. "°C"
elseif attrib == "Rotation Rate" then
smart_info.rota_rate = val
elseif attrib == "SATA Version is" then
smart_info.sata_ver = val
end
end
return smart_info
end
local parse_parted_info = function(keys, line)
-- parse the output of parted command (machine parseable format)
-- /dev/sda:5860533168s:scsi:512:4096:gpt:ATA ST3000DM001-1ER1:;
-- 1:34s:2047s:2014s:free;
-- 1:2048s:1073743872s:1073741825s:ext4:primary:;
local result = {}
local values = {}
for value in line:gmatch("(.-)[:;]") do table.insert(values, value) end
for i = 1,#keys do
result[keys[i]] = values[i] or ""
end
return result
end
local is_raid_member = function(partition)
-- check if inuse as raid member
if nixio.fs.access("/proc/mdstat") then
for _, result in ipairs(luci.util.execl("grep md /proc/mdstat | sed 's/[][]//g'")) do
local md, buf
md, buf = result:match("(md.-):(.+)")
if buf:match(partition) then
return "Raid Member: ".. md
end
end
end
return nil
end
local get_mount_point = function(partition)
local mount_point
for m in mounts:gmatch("/dev/"..partition.." on ([^ ]*)") do
mount_point = (mount_point and (mount_point .. " ") or "") .. m
end
if mount_point then return mount_point end
-- result = luci.sys.exec('cat /proc/mounts | awk \'{if($1=="/dev/'.. partition ..'") print $2}\'')
-- if result ~= "" then return result end
if swaps:match("\n/dev/" .. partition .."%s") then return "swap" end
-- result = luci.sys.exec("cat /proc/swaps | grep /dev/" .. partition)
-- if result ~= "" then return "swap" end
return is_raid_member(partition)
end
-- return used, free, usage
local get_partition_usage = function(partition)
if not nixio.fs.access("/dev/"..partition) then return false end
local used, free, usage = df:match("\n/dev/" .. partition .. "%s+%d+%s+(%d+)%s+(%d+)%s+(%d+)%%%s-")
usage = usage and (usage .. "%") or "-"
used = used and (tonumber(used) * 1024) or 0
free = free and (tonumber(free) * 1024) or 0
return used, free, usage
end
local get_parted_info = function(device)
if not device then return end
local result = {partitions={}}
local DEVICE_INFO_KEYS = { "path", "size", "type", "logic_sec", "phy_sec", "p_table", "model", "flags" }
local PARTITION_INFO_KEYS = { "number", "sec_start", "sec_end", "size", "fs", "tag_name", "flags" }
local partition_temp
local partitions_temp = {}
local disk_temp
for line in luci.util.execi(d.command.parted .. " -s -m /dev/" .. device .. " unit s print free", "r") do
if line:find("^/dev/"..device..":.+") then
disk_temp = parse_parted_info(DEVICE_INFO_KEYS, line)
disk_temp.partitions = {}
if disk_temp["size"] then
local length = disk_temp["size"]:gsub("^(%d+)s$", "%1")
local newsize = tostring(tonumber(length)*tonumber(disk_temp["logic_sec"]))
disk_temp["size"] = newsize
end
if disk_temp["p_table"] == "msdos" then
disk_temp["p_table"] = "MBR"
else
disk_temp["p_table"] = disk_temp["p_table"]:upper()
end
elseif line:find("^%d-:.+") then
partition_temp = parse_parted_info(PARTITION_INFO_KEYS, line)
-- use human-readable form instead of sector number
if partition_temp["size"] then
local length = partition_temp["size"]:gsub("^(%d+)s$", "%1")
local newsize = (tonumber(length) * tonumber(disk_temp["logic_sec"]))
partition_temp["size"] = newsize
partition_temp["size_formated"] = byte_format(newsize)
end
partition_temp["number"] = tonumber(partition_temp["number"]) or -1
if partition_temp["fs"] == "free" then
partition_temp["number"] = -1
partition_temp["fs"] = "Free Space"
partition_temp["name"] = "-"
elseif device:match("sd") or device:match("sata") then
partition_temp["name"] = device..partition_temp["number"]
elseif device:match("mmcblk") or device:match("md") or device:match("nvme") then
partition_temp["name"] = device.."p"..partition_temp["number"]
end
if partition_temp["number"] > 0 and partition_temp["fs"] == "" and d.command.lsblk then
partition_temp["fs"] = luci.util.exec(d.command.lsblk .. " /dev/"..device.. tostring(partition_temp["number"]) .. " -no fstype"):match("([^%s]+)") or ""
end
partition_temp["fs"] = partition_temp["fs"] == "" and "raw" or partition_temp["fs"]
partition_temp["sec_start"] = partition_temp["sec_start"] and partition_temp["sec_start"]:sub(1,-2)
partition_temp["sec_end"] = partition_temp["sec_end"] and partition_temp["sec_end"]:sub(1,-2)
partition_temp["mount_point"] = partition_temp["name"]~="-" and get_mount_point(partition_temp["name"]) or "-"
if partition_temp["mount_point"]~="-" then
partition_temp["used"], partition_temp["free"], partition_temp["usage"] = get_partition_usage(partition_temp["name"])
partition_temp["used_formated"] = partition_temp["used"] and byte_format(partition_temp["used"]) or "-"
partition_temp["free_formated"] = partition_temp["free"] and byte_format(partition_temp["free"]) or "-"
else
partition_temp["used"], partition_temp["free"], partition_temp["usage"] = 0,0,"-"
partition_temp["used_formated"] = "-"
partition_temp["free_formated"] = "-"
end
-- if disk_temp["p_table"] == "MBR" and (partition_temp["number"] < 4) and (partition_temp["number"] > 0) then
-- local real_size_sec = tonumber(nixio.fs.readfile("/sys/block/"..device.."/"..partition_temp["name"].."/size")) * tonumber(disk_temp.phy_sec)
-- if real_size_sec ~= partition_temp["size"] then
-- disk_temp["extended_partition_index"] = partition_temp["number"]
-- partition_temp["type"] = "extended"
-- partition_temp["size"] = real_size_sec
-- partition_temp["fs"] = "-"
-- partition_temp["logicals"] = {}
-- else
-- partition_temp["type"] = "primary"
-- end
-- end
table.insert(partitions_temp, partition_temp)
end
end
if disk_temp and disk_temp["p_table"] == "MBR" then
for i, p in ipairs(partitions_temp) do
if disk_temp["extended_partition_index"] and p["number"] > 4 then
if tonumber(p["sec_end"]) <= tonumber(partitions_temp[disk_temp["extended_partition_index"]]["sec_end"]) and tonumber(p["sec_start"]) >= tonumber(partitions_temp[disk_temp["extended_partition_index"]]["sec_start"]) then
p["type"] = "logical"
table.insert(partitions_temp[disk_temp["extended_partition_index"]]["logicals"], i)
end
elseif (p["number"] < 4) and (p["number"] > 0) then
local s = nixio.fs.readfile("/sys/block/"..device.."/"..p["name"].."/size")
if s then
local real_size_sec = tonumber(s) * tonumber(disk_temp.phy_sec)
-- if size not equal, it's an extended
if real_size_sec ~= p["size"] then
disk_temp["extended_partition_index"] = i
p["type"] = "extended"
p["size"] = real_size_sec
p["fs"] = "-"
p["logicals"] = {}
else
p["type"] = "primary"
end
else
-- if not found in "/sys/block"
p["type"] = "primary"
end
end
end
end
result = disk_temp
result.partitions = partitions_temp
return result
end
local mddetail = function(mdpath)
local detail = {}
local path = mdpath:match("^/dev/md%d+$")
if path then
local mdadm = io.popen(d.command.mdadm .. " --detail "..path, "r")
for line in mdadm:lines() do
local key, value = line:match("^%s*(.+) : (.+)")
if key then
detail[key] = value
end
end
mdadm:close()
end
return detail
end
-- return {{device="", mount_points="", fs="", mount_options="", dump="", pass=""}..}
d.get_mount_points = function()
local mount
local res = {}
local h ={"device", "mount_point", "fs", "mount_options", "dump", "pass"}
for mount in proc_mounts:gmatch("[^\n]+") do
local device = mount:match("^([^%s]+)%s+.+")
-- only show /dev/xxx device
if device and device:match("/dev/") then
res[#res+1] = {}
local i = 0
for v in mount:gmatch("[^%s]+") do
i = i + 1
res[#res][h[i]] = v
end
end
end
return res
end
d.get_disk_info = function(device, wakeup)
--[[ return:
{
path, model, sn, size, size_mounted, flags, type, temp, p_table, logic_sec, phy_sec, sec_size, sata_ver, rota_rate, status, health,
partitions = {
1 = { number, name, sec_start, sec_end, size, size_mounted, fs, tag_name, type, flags, mount_point, usage, used, free, used_formated, free_formated},
2 = { number, name, sec_start, sec_end, size, size_mounted, fs, tag_name, type, flags, mount_point, usage, used, free, used_formated, free_formated},
...
}
--raid devices only
level, members, members_str
}
--]]
if not device then return end
local disk_info
local smart_info = get_smart_info(device)
-- check if divice is the member of raid
smart_info["p_table"] = is_raid_member(device..'0')
-- if status is not active(standby), only check smart_info.
-- if only weakup == true, weakup the disk and check parted_info.
if smart_info.status ~= "STANDBY" or wakeup or (smart_info["p_table"] and not smart_info["p_table"]:match("Raid")) or device:match("^md") then
disk_info = get_parted_info(device)
disk_info["sec_size"] = disk_info["logic_sec"] .. "/" .. disk_info["phy_sec"]
disk_info["size_formated"] = byte_format(tonumber(disk_info["size"]))
-- if status is standby, then get smart_info again
if smart_info.status ~= "ACTIVE" then smart_info = get_smart_info(device) end
else
disk_info = {}
end
for k, v in pairs(smart_info) do
disk_info[k] = v
end
if disk_info.type and disk_info.type:match("md") then
local raid_info = d.list_raid_devices()[disk_info["path"]:match("/dev/(.+)")]
for k, v in pairs(raid_info) do
disk_info[k] = v
end
end
return disk_info
end
d.list_raid_devices = function()
local fs = require "nixio.fs"
local raid_devices = {}
if not fs.access("/proc/mdstat") then return raid_devices end
local mdstat = io.open("/proc/mdstat", "r")
for line in mdstat:lines() do
-- md1 : active raid1 sdb2[1] sda2[0]
-- md127 : active raid5 sdh1[6] sdg1[4] sdf1[3] sde1[2] sdd1[1] sdc1[0]
local device_info = {}
local mdpath, list = line:match("^(md%d+) : (.+)")
if mdpath then
local members = {}
for member in string.gmatch(list, "%S+") do
member_path = member:match("^(%S+)%[%d+%]")
if member_path then
member = '/dev/'..member_path
end
table.insert(members, member)
end
local active = table.remove(members, 1)
local level = "-"
if active == "active" then
level = table.remove(members, 1)
end
local size = tonumber(fs.readfile(string.format("/sys/class/block/%s/size", mdpath)))
local ss = tonumber(fs.readfile(string.format("/sys/class/block/%s/queue/logical_block_size", mdpath)))
device_info["path"] = "/dev/"..mdpath
device_info["size"] = size*ss
device_info["size_formated"] = byte_format(size*ss)
device_info["active"] = active:upper()
device_info["level"] = level
device_info["members"] = members
device_info["members_str"] = table.concat(members, ", ")
-- Get more info from output of mdadm --detail
local detail = mddetail(device_info["path"])
device_info["status"] = detail["State"]:upper()
raid_devices[mdpath] = device_info
end
end
mdstat:close()
return raid_devices
end
-- Collect Devices information
--[[ return:
{
sda={
path, model, inuse, size_formated,
partitions={
{ name, inuse, size_formated }
...
}
}
..
}
--]]
d.list_devices = function()
local fs = require "nixio.fs"
-- get all device names (sdX and mmcblkX)
local target_devnames = {}
for dev in fs.dir("/dev") do
if dev:match("^sd[a-z]$")
or dev:match("^mmcblk%d+$")
or dev:match("^sata[a-z]$")
or dev:match("^nvme%d+n%d+$")
then
table.insert(target_devnames, dev)
end
end
local devices = {}
for i, bname in pairs(target_devnames) do
local device_info = {}
local device = "/dev/" .. bname
local size = tonumber(fs.readfile(string.format("/sys/class/block/%s/size", bname)) or "0")
local ss = tonumber(fs.readfile(string.format("/sys/class/block/%s/queue/logical_block_size", bname)) or "0")
local model = fs.readfile(string.format("/sys/class/block/%s/device/model", bname))
local partitions = {}
for part in nixio.fs.glob("/sys/block/" .. bname .."/" .. bname .. "*") do
local pname = nixio.fs.basename(part)
local psize = byte_format(tonumber(nixio.fs.readfile(part .. "/size"))*ss)
local mount_point = get_mount_point(pname)
if mount_point then device_info["inuse"] = true end
table.insert(partitions, {name = pname, size_formated = psize, inuse = mount_point})
end
device_info["path"] = device
device_info["size_formated"] = byte_format(size*ss)
device_info["model"] = model
device_info["partitions"] = partitions
-- true or false
device_info["inuse"] = device_info["inuse"] or get_mount_point(bname)
local udevinfo = {}
if luci.sys.exec("which udevadm") ~= "" then
local udevadm = io.popen("udevadm info --query=property --name="..device)
for attr in udevadm:lines() do
local k, v = attr:match("(%S+)=(%S+)")
udevinfo[k] = v
end
udevadm:close()
device_info["info"] = udevinfo
if udevinfo["ID_MODEL"] then device_info["model"] = udevinfo["ID_MODEL"] end
end
devices[bname] = device_info
end
-- luci.util.perror(luci.util.serialize_json(devices))
return devices
end
-- get formart cmd
d.get_format_cmd = function()
local AVAILABLE_FMTS = {
ext2 = { cmd = "mkfs.ext2", option = "-F -E lazy_itable_init=1" },
ext3 = { cmd = "mkfs.ext3", option = "-F -E lazy_itable_init=1" },
ext4 = { cmd = "mkfs.ext4", option = "-F -E lazy_itable_init=1" },
fat32 = { cmd = "mkfs.vfat", option = "-F" },
exfat = { cmd = "mkexfat", option = "-f" },
hfsplus = { cmd = "mkhfs", option = "-f" },
ntfs = { cmd = "mkntfs", option = "-f" },
swap = { cmd = "mkswap", option = "" },
btrfs = { cmd = "mkfs.btrfs", option = "-f" }
}
result = {}
for fmt, obj in pairs(AVAILABLE_FMTS) do
local cmd = luci.sys.exec("/usr/bin/which " .. obj["cmd"])
if cmd:match(obj["cmd"]) then
result[fmt] = { cmd = cmd:match("^.+"..obj["cmd"]) ,option = obj["option"] }
end
end
return result
end
d.create_raid = function(rname, rlevel, rmembers)
local mb = {}
for _, v in ipairs(rmembers) do
mb[v]=v
end
rmembers = {}
for _, v in pairs(mb) do
table.insert(rmembers, v)
end
if type(rname) == "string" then
if rname:match("^md%d-%s+") then
rname = "/dev/"..rname:match("^(md%d-)%s+")
elseif rname:match("^/dev/md%d-%s+") then
rname = "/dev/"..rname:match("^(/dev/md%d-)%s+")
elseif not rname:match("/") then
rname = "/dev/md/".. rname
else
return "ERR: Invalid raid name"
end
else
local mdnum = 0
for num=1,127 do
local md = io.open("/dev/md"..tostring(num), "r")
if md == nil then
mdnum = num
break
else
io.close(md)
end
end
if mdnum == 0 then return "ERR: Cannot find proper md number" end
rname = "/dev/md"..mdnum
end
if rlevel == "5" or rlevel == "6" then
if #rmembers < 3 then return "ERR: Not enough members" end
end
if rlevel == "10" then
if #rmembers < 4 then return "ERR: Not enough members" end
end
if #rmembers < 2 then return "ERR: Not enough members" end
local cmd = d.command.mdadm .. " --create "..rname.." --run --assume-clean --homehost=any --level=" .. rlevel .. " --raid-devices=" .. #rmembers .. " " .. table.concat(rmembers, " ")
local res = luci.util.exec(cmd)
return res
end
d.gen_mdadm_config = function()
if not nixio.fs.access("/etc/config/mdadm") then return end
local uci = require "luci.model.uci"
local x = uci.cursor()
-- delete all array sections
x:foreach("mdadm", "array", function(s) x:delete("mdadm",s[".name"]) end)
local cmd = d.command.mdadm .. " -D -s"
--ARRAY /dev/md1 metadata=1.2 name=any:1 UUID=f998ae14:37621b27:5c49e850:051f6813
--ARRAY /dev/md3 metadata=1.2 name=any:3 UUID=c068c141:4b4232ca:f48cbf96:67d42feb
for _, v in ipairs(luci.util.execl(cmd)) do
local device, uuid = v:match("^ARRAY%s-([^%s]+)%s-[^%s]-%s-[^%s]-%s-UUID=([^%s]+)%s-")
if device and uuid then
local section_name = x:add("mdadm", "array")
x:set("mdadm", section_name, "device", device)
x:set("mdadm", section_name, "uuid", uuid)
end
end
x:commit("mdadm")
-- enable mdadm
luci.util.exec("/etc/init.d/mdadm enable")
end
-- list btrfs filesystem device
-- {uuid={uuid, label, members, size, used}...}
d.list_btrfs_devices = function()
local btrfs_device = {}
if not d.command.btrfs then return btrfs_device end
local line, _uuid
for _, line in ipairs(luci.util.execl(d.command.btrfs .. " filesystem show -d --raw"))
do
local label, uuid = line:match("^Label:%s+([^%s]+)%s+uuid:%s+([^%s]+)")
if label and uuid then
_uuid = uuid
local _label = label:match("^'([^']+)'")
btrfs_device[_uuid] = {label = _label or label, uuid = uuid}
-- table.insert(btrfs_device, {label = label, uuid = uuid})
end
local used = line:match("Total devices[%w%s]+used%s+(%d+)$")
if used then
btrfs_device[_uuid]["used"] = tonumber(used)
btrfs_device[_uuid]["used_formated"] = byte_format(tonumber(used))
end
local size, device = line:match("devid[%w.%s]+size%s+(%d+)[%w.%s]+path%s+([^%s]+)$")
if size and device then
btrfs_device[_uuid]["size"] = btrfs_device[_uuid]["size"] and btrfs_device[_uuid]["size"] + tonumber(size) or tonumber(size)
btrfs_device[_uuid]["size_formated"] = byte_format(btrfs_device[_uuid]["size"])
btrfs_device[_uuid]["members"] = btrfs_device[_uuid]["members"] and btrfs_device[_uuid]["members"]..", "..device or device
end
end
return btrfs_device
end
d.create_btrfs = function(blabel, blevel, bmembers)
-- mkfs.btrfs -L label -d blevel /dev/sda /dev/sdb
if not d.command.btrfs or type(bmembers) ~= "table" or next(bmembers) == nil then return "ERR no btrfs support or no members" end
local label = blabel and " -L " .. blabel or ""
local cmd = "mkfs.btrfs -f " .. label .. " -d " .. blevel .. " " .. table.concat(bmembers, " ")
return luci.util.exec(cmd)
end
-- get btrfs info
-- {uuid, label, members, data_raid_level,metadata_raid_lavel, size, used, size_formated, used_formated, free, free_formated, usage}
d.get_btrfs_info = function(m_point)
local btrfs_info = {}
if not m_point or not d.command.btrfs then return btrfs_info end
local cmd = d.command.btrfs .. " filesystem show --raw " .. m_point
local _, line, uuid, _label, members
for _, line in ipairs(luci.util.execl(cmd)) do
if not uuid and not _label then
_label, uuid = line:match("^Label:%s+([^%s]+)%s+uuid:%s+([^s]+)")
else
local mb = line:match("%s+devid.+path%s+([^%s]+)")
if mb then
members = members and (members .. ", ".. mb) or mb
end
end
end
if not _label or not uuid then return btrfs_info end
local label = _label:match("^'([^']+)'")
cmd = d.command.btrfs .. " filesystem usage -b " .. m_point
local used, free, data_raid_level, metadata_raid_lavel
for _, line in ipairs(luci.util.execl(cmd)) do
if not used then
used = line:match("^%s+Used:%s+(%d+)")
elseif not free then
free = line:match("^%s+Free %(estimated%):%s+(%d+)")
elseif not data_raid_level then
data_raid_level = line:match("^Data,%s-(%w+)")
elseif not metadata_raid_lavel then
metadata_raid_lavel = line:match("^Metadata,%s-(%w+)")
end
end
if used and free and data_raid_level and metadata_raid_lavel then
used = tonumber(used)
free = tonumber(free)
btrfs_info = {
uuid = uuid,
label = label,
data_raid_level = data_raid_level,
metadata_raid_lavel = metadata_raid_lavel,
used = used,
free = free,
size = used + free,
size_formated = byte_format(used + free),
used_formated = byte_format(used),
free_formated = byte_format(free),
members = members,
usage = string.format("%.2f",(used / (free+used) * 100)) .. "%"
}
end
return btrfs_info
end
-- get btrfs subvolume
-- {id={id, gen, top_level, path, snapshots, otime, default_subvolume}...}
d.get_btrfs_subv = function(m_point, snapshot)
local subvolume = {}
if not m_point or not d.command.btrfs then return subvolume end
-- get default subvolume
local cmd = d.command.btrfs .. " subvolume get-default " .. m_point
local res = luci.util.exec(cmd)
local default_subvolume_id = res:match("^ID%s+([^%s]+)")
-- get the root subvolume
if not snapshot then
local _, line, section_snap, _uuid, _otime, _id, _snap
cmd = d.command.btrfs .. " subvolume show ".. m_point
for _, line in ipairs(luci.util.execl(cmd)) do
if not section_snap then
if not _uuid then
_uuid = line:match("^%s-UUID:%s+([^%s]+)")
elseif not _otime then
_otime = line:match("^%s+Creation time:%s+(.+)")
elseif not _id then
_id = line:match("^%s+Subvolume ID:%s+([^%s]+)")
elseif line:match("^%s+(Snapshot%(s%):)") then
section_snap = true
end
else
local snapshot = line:match("^%s+(.+)")
if snapshot then
_snap = _snap and (_snap ..", /".. snapshot) or ("/"..snapshot)
end
end
end
if _uuid and _otime and _id then
subvolume["0".._id] = {id = _id , uuid = _uuid, otime = _otime, snapshots = _snap, path = "/"}
if default_subvolume_id == _id then
subvolume["0".._id].default_subvolume = 1
end
end
end
-- get subvolume of btrfs
cmd = d.command.btrfs .. " subvolume list -gcu" .. (snapshot and "s " or " ") .. m_point
for _, line in ipairs(luci.util.execl(cmd)) do
-- ID 259 gen 11 top level 258 uuid 26ae0c59-199a-cc4d-bd58-644eb4f65d33 path 1a/2b'
local id, gen, top_level, uuid, path, otime, otime2
if snapshot then
id, gen, top_level, otime, otime2, uuid, path = line:match("^ID%s+([^%s]+)%s+gen%s+([^%s]+)%s+cgen.-top level%s+([^%s]+)%s+otime%s+([^%s]+)%s+([^%s]+)%s+uuid%s+([^%s]+)%s+path%s+([^%s]+)%s-$")
else
id, gen, top_level, uuid, path = line:match("^ID%s+([^%s]+)%s+gen%s+([^%s]+)%s+cgen.-top level%s+([^%s]+)%s+uuid%s+([^%s]+)%s+path%s+([^%s]+)%s-$")
end
if id and gen and top_level and uuid and path then
subvolume[id] = {id = id, gen = gen, top_level = top_level, otime = (otime and otime or "") .." ".. (otime2 and otime2 or ""), uuid = uuid, path = '/'.. path}
if not snapshot then
-- use btrfs subv show to get snapshots
local show_cmd = d.command.btrfs .. " subvolume show "..m_point.."/"..path
local __, line_show, section_snap
for __, line_show in ipairs(luci.util.execl(show_cmd)) do
if not section_snap then
local create_time = line_show:match("^%s+Creation time:%s+(.+)")
if create_time then
subvolume[id]["otime"] = create_time
elseif line_show:match("^%s+(Snapshot%(s%):)") then
section_snap = "true"
end
else
local snapshot = line_show:match("^%s+(.+)")
subvolume[id]["snapshots"] = subvolume[id]["snapshots"] and (subvolume[id]["snapshots"] .. ", /".. snapshot) or ("/"..snapshot)
end
end
end
end
end
if subvolume[default_subvolume_id] then
subvolume[default_subvolume_id].default_subvolume = 1
end
-- if m_point == "/tmp/.btrfs_tmp" then
-- luci.util.exec("umount " .. m_point)
-- end
return subvolume
end
d.format_partition = function(partition, fs)
local partition_name = "/dev/".. partition
if not nixio.fs.access(partition_name) then
return 500, "Partition NOT found!"
end
local format_cmd = d.get_format_cmd()
if not format_cmd[fs] then
return 500, "Filesystem NOT support!"
end
local cmd = format_cmd[fs].cmd .. " " .. format_cmd[fs].option .. " " .. partition_name
local res = luci.util.exec(cmd .. " 2>&1")
if res and res:lower():match("error+") then
return 500, res
else
return 200, "OK"
end
end
return d

View File

@ -1,7 +0,0 @@
<%+cbi/valueheader%>
<% if self:cfgvalue(section) ~= false then %>
<input class="cbi-button cbi-button-<%=self.inputstyle or "button" %>" type="submit"<%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.inputtitle or self.title)%> <% if self.view_disabled then %> disabled <% end %>/>
<% else %>
-
<% end %>
<%+cbi/valuefooter%>

View File

@ -1,7 +0,0 @@
<%+cbi/valueheader%>
<% if self:cfgvalue(section) ~= false then %>
<input class="cbi-button cbi-button-<%=self.inputstyle or "button" %>" onclick="event.preventDefault();partition_format('<%=self.partitions[section].name%>', '<%=self.format_cmd%>', '<%=self.inputtitle%>');" type="submit"<%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.inputtitle or self.title)%> <% if self.view_disabled then %> disabled <% end %>/>
<% else %>
-
<% end %>
<%+cbi/valuefooter%>

View File

@ -1,7 +0,0 @@
<div style="display: inline-block;">
<% if self:cfgvalue(section) ~= false then %>
<input class="cbi-button cbi-button-<%=self.inputstyle or "button" %>" type="submit"" <% if self.disable then %>disabled <% end %><%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.inputtitle or self.title)%> />
<% else %>
-
<% end %>
</div>

View File

@ -1,37 +0,0 @@
<div class="cbi-section" id="cbi-<%=self.config%>-section">
<% if self.title and #self.title > 0 then -%>
<legend><%=self.title%></legend>
<%- end %>
<% if self.description and #self.description > 0 then -%>
<div class="cbi-section-descr"><%=self.description%></div>
<%- end %>
<div class="cbi-section-node">
<div id="cbi-<%=self.config%>-<%=tostring(self):sub(8)%>">
<% self:render_children(1, scope or {}) %>
</div>
<% if self.error and self.error[1] then -%>
<div class="cbi-section-error">
<ul><% for _, e in ipairs(self.error[1]) do -%>
<li>
<%- if e == "invalid" then -%>
<%:One or more fields contain invalid values!%>
<%- elseif e == "missing" then -%>
<%:One or more required fields have no value!%>
<%- else -%>
<%=pcdata(e)%>
<%- end -%>
</li>
<%- end %></ul>
</div>
<%- end %>
</div>
</div>
<%-
if type(self.hidden) == "table" then
for k, v in pairs(self.hidden) do
-%>
<input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" />
<%-
end
end
%>

View File

@ -1,88 +0,0 @@
<% if not self.embedded then %>
<form method="post" enctype="multipart/form-data" action="<%=REQUEST_URI%>"<%=
attr("data-strings", luci.util.serialize_json({
label = {
choose = translate('-- Please choose --'),
custom = translate('-- custom --'),
},
path = {
resource = resource,
browser = url("admin/filebrowser")
}
}))
%>>
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
<input type="hidden" name="token" value="<%=token%>" />
<input type="hidden" name="cbi.submit" value="1" /><%
end
%><div class="cbi-map" id="cbi-<%=self.config%>"><%
if self.title and #self.title > 0 then
%><h2 name="content"><%=self.title%></h2><%
end
if self.description and #self.description > 0 then
%><div class="cbi-map-descr"><%=self.description%></div><%
end
self:render_children()
%></div><%
if self.message then
%><div class="alert-message notice"><%=self.message%></div><%
end
if self.errmessage then
%><div class="alert-message warning"><%=self.errmessage%></div><%
end
if not self.embedded then
if type(self.hidden) == "table" then
local k, v
for k, v in pairs(self.hidden) do
%><input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" /><%
end
end
local display_back = (self.redirect)
local display_cancel = (self.cancel ~= false and self.on_cancel)
local display_skip = (self.flow and self.flow.skip)
local display_submit = (self.submit ~= false)
local display_reset = (self.reset ~= false)
if display_back or display_cancel or display_skip or display_submit or display_reset then
%><div class="cbi-page-actions"><%
if display_back then
%><input class="cbi-button cbi-button-link" type="button" value="<%:Back to Overview%>" onclick="location.href='<%=pcdata(self.redirect)%>'" /> <%
end
if display_cancel then
local label = pcdata(self.cancel or translate("Cancel"))
%><input class="cbi-button cbi-button-link" type="button" value="<%=label%>" onclick="cbi_submit(this, 'cbi.cancel')" /> <%
end
if display_skip then
%><input class="cbi-button cbi-button-neutral" type="button" value="<%:Skip%>" onclick="cbi_submit(this, 'cbi.skip')" /> <%
end
if display_submit then
local label = pcdata(self.submit or translate("Submit"))
%><input class="cbi-button cbi-button-save" type="submit" value="<%=label%>" /> <%
end
if display_reset then
local label = pcdata(self.reset or translate("Reset"))
%><input class="cbi-button cbi-button-reset" type="reset" value="<%=label%>" /> <%
end
%></div><%
end
%></form><%
end
%>
<script type="text/javascript">cbi_init();</script>

View File

@ -1,108 +0,0 @@
<script type="text/javascript">
window.onload = function () {
//disk partition info
let p_colors = ["#c0c0ff", "#fbbd00", "#e97c30", "#a0e0a0", "#e0c0ff"]
let lines = document.querySelectorAll('[id^=cbi-disk-]')
lines.forEach((item) => {
let dev = item.id.match(/cbi-disk-(.*)/)[1]
if (dev == "table") { return }
XHR.get('<%=luci.dispatcher.build_url("admin/system/diskman/get_disk_info")%>/' + dev, null, (x, disk_info) => {
// handle disk info
item.childNodes.forEach((cell) => {
if (cell && cell.attributes) {
if (cell.getAttribute("data-name") == "sn" || cell.childNodes[1] && cell.childNodes[1].id.match(/sn/)) {
cell.innerText = disk_info.sn || "-"
} else if (cell.getAttribute("data-name") == "temp" || cell.childNodes[1] && cell.childNodes[1].id.match(/temp/)) {
cell.innerText = disk_info.temp || "-"
} else if (cell.getAttribute("data-name") == "p_table" || cell.childNodes[1] && cell.childNodes[1].id.match(/p_table/)) {
cell.innerText = disk_info.p_table || "-"
} else if (cell.getAttribute("data-name") == "sata_ver" || cell.childNodes[1] && cell.childNodes[1].id.match(/sata_ver/)) {
cell.innerText = disk_info.sata_ver || "-"
} else if (cell.getAttribute("data-name") == "health" || cell.childNodes[1] && cell.childNodes[1].id.match(/health/)) {
cell.innerText = disk_info.health || "-"
} else if (cell.getAttribute("data-name") == "status" || cell.childNodes[1] && cell.childNodes[1].id.match(/status/)) {
cell.innerText = disk_info.status || "-"
}
}
})
// handle partitons info
if (disk_info.partitions && disk_info.partitions.length > 0) {
let partitons_div
if (item.nodeName == "TR") {
partitons_div = '<tr width="100%" style="white-space:nowrap;"><td style="margin:0px; padding:0px; border:0px; white-space:nowrap;" colspan="15">'
} else if (item.nodeName == "DIV") {
partitons_div = '<div class="tr cbi-section-table-row cbi-rowstyle-1"><div style="white-space:nowrap; position:absolute; width:100%">'
}
let expand = 0
let need_expand = 0
disk_info.partitions.forEach((part) => {
let p = part.size / disk_info.size * 100
if (p <= 8) {
expand += 8
need_expand += p
part.part_percent = 8
}
})
let n = 0
disk_info.partitions.forEach((part) => {
let p = part.size / disk_info.size * 100
if (p > 8) {
part.part_percent = p * (100 - expand) / (100 - need_expand)
}
let part_percent = part.part_percent + '%'
let p_color = p_colors[n++]
if (n > 4) { n = 0 }
let inline_txt = (part.name != '-' && part.name || '') + ' ' + (part.fs != 'Free Space' && part.fs || '') + ' ' + part.size_formated + ' ' + (part.useage != '-' && part.useage || '')
let partiton_div = '<div title="' + inline_txt + '" style="display:inline-block; text-align:center;background-color:' + p_color + '; width:' + part_percent + '">' + inline_txt + '</div>'
partitons_div += partiton_div
})
if (item.nodeName == "TR") {
partitons_div += '</td></tr>'
} else if (item.nodeName == "DIV") {
partitons_div += '</div><div>&nbsp</div></div>'
}
item.insertAdjacentHTML('afterend', partitons_div);
}
})
})
//raid table
lines = document.querySelectorAll('[id^=cbi-_raid-]')
lines.forEach((item) => {
let dev = item.id.match(/cbi-_raid-(.*)/)[1]
if (dev == "table") { return }
console.log(dev)
XHR.get('<%=luci.dispatcher.build_url("admin/system/diskman/get_disk_info")%>/' + dev, null, (x, disk_info) => {
// handle raid info
item.childNodes.forEach((cell) => {
if (cell && cell.attributes) {
if (cell.getAttribute("data-name") == "p_table" || cell.childNodes[1] && cell.childNodes[1].id.match(/p_table/)) {
cell.innerText = disk_info.p_table || "-"
}
}
})
// handle partitons info
let partitons_div
if (item.nodeName == "TR") {
partitons_div = '<tr width="100%" style="white-space:nowrap;"><td style="margin:0px; padding:0px; border:0px; white-space:nowrap;" colspan="15">'
} else if (item.nodeName == "DIV") {
partitons_div = '<div class="tr cbi-section-table-row cbi-rowstyle-1"><div style="white-space:nowrap; position:absolute; width:100%">'
}
let n = 0
disk_info.partitions.forEach((part) => {
let part_percent = part.size / disk_info.size * 100 + '%'
let p_color = p_colors[n++]
if (n > 4) { n = 0 }
let inline_txt = (part.name != '-' && part.name || '') + ' ' + (part.fs != 'Free Space' && part.fs || '') + ' ' + part.size_formated + ' ' + (part.useage != '-' && part.useage || '')
let partiton_div = '<div title="' + inline_txt + '" style="display:inline-block; text-align:center;background-color:' + p_color + '; width:' + part_percent + '">' + inline_txt + '</div>'
partitons_div += partiton_div
})
if (item.nodeName == "TR") {
partitons_div += '</td></tr>'
} else if (item.nodeName == "DIV") {
partitons_div += '</div><div>&nbsp</div></div>'
}
item.insertAdjacentHTML('afterend', partitons_div);
})
})
}
</script>

View File

@ -1,129 +0,0 @@
<style type="text/css">
#dialog_format {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
display: none;
z-index: 20000;
}
#dialog_format .dialog_box {
position: relative;
background: rgba(255, 255, 255);
top: 35%;
width: 40%;
min-width: 20em;
margin: auto;
display: flex;
flex-wrap: wrap;
height:auto;
align-items: center;
}
#dialog_format .dialog_line {
margin-top: .5em;
margin-bottom: .5em;
margin-left: 2em;
margin-right: 2em;
}
#dialog_format .dialog_box>h4,
#dialog_format .dialog_box>p,
#dialog_format .dialog_box>div {
flex-basis: 100%;
}
#dialog_format .dialog_box>img {
margin-right: 1em;
flex-basis: 32px;
}
body.dialog-format-active {
overflow: hidden;
height: 100vh;
}
body.dialog-format-active #dialog_format {
display: block;
}
</style>
<script type="text/javascript">//<![CDATA[
function show_detail(dev, e) {
e.preventDefault()
window.open('<%=luci.dispatcher.build_url("admin/system/diskman/smartdetail")%>/' + dev,
'newwindow', 'height=480,width=800,top=100,left=200,toolbar=no,menubar=no,scrollbars=yes, resizable=no,location=no, status=no')
}
window.onload = function () {
// handle partition table
const btn_p_table = document.getElementById("widget.cbid.table.1.p_table") || document.getElementById("cbid.table.1.p_table")
const btn_p_table_raw_index = btn_p_table.selectedIndex
const val_name = document.getElementById("cbi-table-1-path").innerText.split('/').pop()
btn_p_table.onchange = function () {
let btn_p_table_index = btn_p_table.selectedIndex
if (btn_p_table_index != btn_p_table_raw_index) {
if (confirm("<%:Warnning !! \nTHIS WILL OVERWRITE EXISTING PARTITIONS!! \nModify the partition table?%>")) {
let p_table = btn_p_table.options[btn_p_table_index].value
XHR.get('<%=luci.dispatcher.build_url("admin/system/diskman/mk_p_table")%>', { dev: val_name, p_table: p_table }, (x, res) => {
if (res.code == 0) {
location.reload();
}
}
);
}
else {
}
}
}
// handle smartinfo
const url = location.href.split('/')
const dev = url[url.length - 1]
const btn_smart_detail = document.getElementById("cbi-table-1-health")
btn_smart_detail.children[0].onclick = show_detail.bind(this, dev)
}
function close_dialog() {
document.body.classList.remove('dialog-format-active')
document.documentElement.style.overflowY = 'scroll'
}
function do_format(partation_name){
let fs = document.getElementById("filesystem_list").value
let status = document.getElementById("format-status")
if(!fs) {
status.innerHTML = "<%:Please select file system!%>"
return
}
status.innerHTML = "<%:Formatting..%>"
let b = document.getElementById('btn_format')
b.disabled = true
let xhr = new XHR()
xhr.post('<%=luci.dispatcher.build_url("admin/system/diskman/format_partition")%>', { partation_name: partation_name, file_system: fs }, (x, res) => {
if (x.status == 200) {
status.innerHTML = x.statusText
location.reload();
}else{
status.innerHTML = x.statusText
}
})
}
function clear_text(){
let s = document.getElementById('format-status')
s.innerHTML = ""
let b = document.getElementById('btn_format')
b.disabled = false
}
function partition_format(partition_name, format_cmd, current_fs){
let list = ''
format_cmd.split(",").forEach(e => {
list = list + '<option value="'+e+'">'+e+'</option>'
});
document.getElementById('dialog_format') || document.body.insertAdjacentHTML("beforeend", '<div id="dialog_format"><div class="dialog_box"><div class="dialog_line"></div><div class="dialog_line"><span><%:Format partation:%> <b>'+partition_name+'</b></span><br><span id="format-status" style="color: red;"></span></div><div class="dialog_line"><select id="filesystem_list" class="cbi-input-select" onchange="clear_text()">'+list+'</select></div><div class="dialog_line" style="text-align: right;"><input type="button" class="cbi-button cbi-button-apply" id="btn_format" type="submit" value="<%:Format%>" onclick="do_format(`'+partition_name+'`)" /> <input type="button"class="cbi-button cbi-button-reset" type="reset" value="<%:Cancel%>" onclick="close_dialog()" /></div><div class="dialog_line"></div></div></div>>')
document.body.classList.add('dialog-format-active')
document.documentElement.style.overflowY = 'hidden'
let fs_list = document.getElementById("filesystem_list")
fs_list.value = current_fs
}
</script>

View File

@ -1,79 +0,0 @@
<html>
<head>
<title>S.M.A.R.T detail of <%=dev%></title>
<link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/cascade.css" />
<script type="text/javascript">//<![CDATA[
let formData = new FormData()
let xhr = new XMLHttpRequest()
xhr.open("GET", '<%=luci.dispatcher.build_url("admin", "system", "diskman", "smartattr", dev)%>', true)
xhr.onload = function () {
let st = JSON.parse(xhr.responseText)
let tb = document.getElementById('smart_attr_table');
if (st && tb) {
/* clear all rows */
while (tb.rows.length > 1)
tb.deleteRow(1);
for (var i = 0; i < st.length; i++) {
var tr = tb.insertRow(-1);
tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1);
var td = null
<% if dev: match("nvme") then %>
tr.insertCell(-1).innerHTML = st[i].key;
tr.insertCell(-1).innerHTML = st[i].value;
<% else %>
tr.insertCell(-1).innerHTML = st[i].id;
tr.insertCell(-1).innerHTML = st[i].attrbute;
tr.insertCell(-1).innerHTML = st[i].flag;
tr.insertCell(-1).innerHTML = st[i].value;
tr.insertCell(-1).innerHTML = st[i].worst;
tr.insertCell(-1).innerHTML = st[i].thresh;
tr.insertCell(-1).innerHTML = st[i].type;
tr.insertCell(-1).innerHTML = st[i].updated;
tr.insertCell(-1).innerHTML = st[i].raw;
if ((st[i].id == '05' || st[i].id == 'C5') && st[i].raw != '0') {
tr.style.cssText = "background-color:red !important;";
}
<% end %>
}
if (tb.rows.length == 1) {
var tr = tb.insertRow(-1);
tr.className = 'cbi-section-table-row';
var td = tr.insertCell(-1);
td.colSpan = 4;
td.innerHTML = '<em><br /><%:No Attrbute to display.%></em>';
}
}
}
xhr.send(formData)
//]]></script>
</head>
<body>
<div id="maincontainer">
<fieldset class="cbi-section">
<legend><%:S.M.A.R.T Attrbutes%>: /dev/<%=dev%></legend>
<table class="cbi-section-table" id="smart_attr_table">
<tr class="cbi-section-table-titles">
<% if dev:match("nvme") then %>
<!-- <th class="cbi-section-table-cell"><%:KEY%></th>
<th class="cbi-section-table-cell"><%:VALUE%></th> -->
<% else %>
<th class="cbi-section-table-cell"><%:ID%></th>
<th class="cbi-section-table-cell"><%:Attrbute%></th>
<th class="cbi-section-table-cell"><%:Flag%></th>
<th class="cbi-section-table-cell"><%:Value%></th>
<th class="cbi-section-table-cell"><%:Worst%></th>
<th class="cbi-section-table-cell"><%:Thresh%></th>
<th class="cbi-section-table-cell"><%:Type%></th>
<th class="cbi-section-table-cell"><%:Updated%></th>
<th class="cbi-section-table-cell"><%:Raw%></th>
<% end %>
</tr>
<tr class="cbi-section-table-row">
<td colspan="4"><em><br /><%:Collecting data...%></em></td>
</tr>
</table>
</fieldset>
</div>
</body>
</html>

View File

@ -1,239 +0,0 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n"
msgid "DiskMan"
msgstr "Menadżer dysków"
msgid "Manage Disks over LuCI."
msgstr "Zarządzaj dyskami przez LuCI."
msgid "Rescan Disks"
msgstr "Skanuj dyski"
msgid "Disks"
msgstr "Dyski"
msgid "Path"
msgstr "Ścieżka"
msgid "Serial Number"
msgstr "Numer seryjny"
msgid "Temp"
msgstr "Temperatura"
msgid "Partition Table"
msgstr "Tablica partycji"
msgid "SATA Version"
msgstr "Wersja SATA"
msgid "Health"
msgstr "Kondycja"
msgid "File System"
msgstr "System plików"
msgid "Mount Options"
msgstr "Opcje montowania"
msgid "Mount"
msgstr "Montuj"
msgid "Umount"
msgstr "Odmontuj"
msgid "Eject"
msgstr "Wysuń"
msgid "New"
msgstr "Nowa"
msgid "Remove"
msgstr "Wysuń"
msgid "Format"
msgstr "Formatuj"
msgid "Start Sector"
msgstr "Sektor początkowy"
msgid "End Sector"
msgstr "Sektor końcowy"
msgid "Usage"
msgstr "Wykorzystane"
msgid "Used"
msgstr "Zajęte"
msgid "Free Space"
msgstr "Wolna przestrzeń"
msgid "Model"
msgstr "Model urządzenia"
msgid "Size"
msgstr "Rozmiar"
msgid "Status"
msgstr "Status"
msgid "Mount Point"
msgstr "Punkt montowania"
msgid "Sector Size"
msgstr "Rozmiar sektora"
msgid "Rotation Rate"
msgstr "Obroty"
msgid "RAID Devices"
msgstr "Urządzenia RAID"
msgid "RAID mode"
msgstr "Tryb RAID"
msgid "Members"
msgstr "Członkowie"
msgid "Active"
msgstr "Aktywny"
msgid "RAID Creation"
msgstr "Kreator RAID"
msgid "Raid Name"
msgstr "Nazwa RAID"
msgid "Raid Level"
msgstr "Poziom RAID"
msgid "Raid Member"
msgstr "Członek RAID"
msgid "Create Raid"
msgstr "Utwórz RAID"
msgid "Partition Management"
msgstr "Menadżer partycji"
msgid "Partition Disk over LuCI."
msgstr "Zarządzaj partycjami przez LuCI."
msgid "Device Info"
msgstr "Informacja o urządzeniu"
msgid "Disk Man"
msgstr "Menadżer dysków"
msgid "Partitions Info"
msgstr "Informacja o partycjach"
msgid "Default 2048 sector alignment, support +size{b,k,m,g,t} in End Sector"
msgstr "Sektory wyrównywane są do wielokrotności 2048, sektor końcowy można podać jako rozmiar w postaci +size{b,k,m,g,t}"
msgid "Multiple Devices Btrfs Creation"
msgstr "Kreator urządzeń Btrfs"
msgid "Label"
msgstr "Etykieta"
msgid "Btrfs Label"
msgstr "Etykieta Btrfs"
msgid "Btrfs Raid Level"
msgstr "Poziom Raid Btrfs"
msgid "Btrfs Member"
msgstr "Członek Btrfs"
msgid "Create Btrfs"
msgstr "Utwórz Btrfs"
msgid "New Snapshot"
msgstr "Nowy obraz"
msgid "SubVolumes"
msgstr "Sub-wolumeny"
msgid "Top Level"
msgstr "Top Level"
msgid "Manage Btrfs"
msgstr "Zarządzaj Btrfs"
msgid "Otime"
msgstr "Otime"
msgid "Snapshots"
msgstr "Obrazy"
msgid "Set Default"
msgstr "Ustaw domyślnie"
msgid "Source Path"
msgstr "Ścieżka źródłowa"
msgid "Readonly"
msgstr "Tylko do odczytu"
msgid "Delete"
msgstr "Usuń"
msgid "Create"
msgstr "Utwórz"
msgid "Destination Path (optional)"
msgstr "Ścieżka docelowa (opcjonalnie)"
msgid "Metadata"
msgstr "Metadata"
msgid "Data"
msgstr "Data"
msgid "Btrfs Info"
msgstr "Informacja o Btrfs"
msgid "The source path for create the snapshot"
msgstr "Ścieżka źródłowa do utworzenia obrazu"
msgid "The path where you want to store the snapshot"
msgstr "Ścieżka w której chcesz przechowywać obraz"
msgid "Please input Source Path of snapshot, Source Path must start with '/'"
msgstr "Proszę podać ścieżkę źródłową dla obrazu, ścieżka musi zaczynać się od '/'"
msgid "Please input Subvolume Path, Subvolume must start with '/'"
msgstr "Proszę podać ścieżkę dla sub-wolumenu, sub-wolumen musi zaczynać się od '/'"
msgid "is in use! please unmount it first!"
msgstr "aktualnie jest w użyciu, proszę najpierw odmontować"
msgid "Partition NOT found!"
msgstr "Nie znaleziono partycji"
msgid "Filesystem NOT support!"
msgstr "System plików nie jest obsługiwany"
msgid "Invalid Start Sector!"
msgstr "Nieprawidłowy sektor początkowy"
msgid "Invalid End Sector"
msgstr "Nieprawidłowy sektor końcowy"
msgid "Partition not exists!"
msgstr "Partycja nie istnieje!"
msgid "Creation"
msgstr "Kreator"
msgid "Please select file system!"
msgstr "Proszę wybrać system plików!"
msgid "Format partation:"
msgstr "Format partycji:"
msgid "Warnning !! \nTHIS WILL OVERWRITE EXISTING PARTITIONS!! \nModify the partition table?"
msgstr "Ostrzeżenie !! \nISTNIEJĄCE PARTYCJE ZOSTANĄ NADPISANE!! \nZmodyfikować tablicę partycji?"

View File

@ -1,239 +0,0 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n"
msgid "DiskMan"
msgstr "DiskMan 磁盘管理"
msgid "Manage Disks over LuCI."
msgstr "通过 LuCI 管理磁盘"
msgid "Rescan Disks"
msgstr "重新扫描磁盘"
msgid "Disks"
msgstr "磁盘"
msgid "Path"
msgstr "路径"
msgid "Serial Number"
msgstr "序列号"
msgid "Temp"
msgstr "温度"
msgid "Partition Table"
msgstr "分区表"
msgid "SATA Version"
msgstr "SATA 版本"
msgid "Health"
msgstr "健康"
msgid "File System"
msgstr "文件系统"
msgid "Mount Options"
msgstr "挂载选项"
msgid "Mount"
msgstr "挂载"
msgid "Umount"
msgstr "卸载"
msgid "Eject"
msgstr "弹出"
msgid "New"
msgstr "创建"
msgid "Remove"
msgstr "移除"
msgid "Format"
msgstr "格式化"
msgid "Start Sector"
msgstr "起始扇区"
msgid "End Sector"
msgstr "中止扇区"
msgid "Usage"
msgstr "用量"
msgid "Used"
msgstr "已使用"
msgid "Free Space"
msgstr "空闲空间"
msgid "Model"
msgstr "型号"
msgid "Size"
msgstr "容量"
msgid "Status"
msgstr "状态"
msgid "Mount Point"
msgstr "挂载点"
msgid "Sector Size"
msgstr "扇区/物理扇区大小"
msgid "Rotation Rate"
msgstr "转速"
msgid "RAID Devices"
msgstr "RAID 设备"
msgid "RAID mode"
msgstr "RAID 模式"
msgid "Members"
msgstr "成员"
msgid "Active"
msgstr "活动"
msgid "RAID Creation"
msgstr "RAID 创建"
msgid "Raid Name"
msgstr "RAID 名称"
msgid "Raid Level"
msgstr "RAID 级别"
msgid "Raid Member"
msgstr "磁盘阵列成员"
msgid "Create Raid"
msgstr "创建 RAID"
msgid "Partition Management"
msgstr "分区管理"
msgid "Partition Disk over LuCI."
msgstr "通过LuCI分区磁盘。"
msgid "Device Info"
msgstr "设备信息"
msgid "Disk Man"
msgstr "磁盘管理"
msgid "Partitions Info"
msgstr "分区信息"
msgid "Default 2048 sector alignment, support +size{b,k,m,g,t} in End Sector"
msgstr "默认2048扇区对齐【中止扇区】支持 +容量{b,k,m,g,t} 格式,例:+500m +10g +1t"
msgid "Multiple Devices Btrfs Creation"
msgstr "Btrfs 阵列创建"
msgid "Label"
msgstr "卷标"
msgid "Btrfs Label"
msgstr "Btrfs 卷标"
msgid "Btrfs Raid Level"
msgstr "Btrfs Raid 级别"
msgid "Btrfs Member"
msgstr "Btrfs 整列成员"
msgid "Create Btrfs"
msgstr "创建 Btrfs"
msgid "New Snapshot"
msgstr "新建快照"
msgid "SubVolumes"
msgstr "子卷"
msgid "Top Level"
msgstr "父ID"
msgid "Manage Btrfs"
msgstr "Btrfs 管理"
msgid "Otime"
msgstr "创建时间"
msgid "Snapshots"
msgstr "快照"
msgid "Set Default"
msgstr "默认子卷"
msgid "Source Path"
msgstr "源目录"
msgid "Readonly"
msgstr "只读"
msgid "Delete"
msgstr "删除"
msgid "Create"
msgstr "创建"
msgid "Destination Path (optional)"
msgstr "目标目录(可选)"
msgid "Metadata"
msgstr "元数据"
msgid "Data"
msgstr "数据"
msgid "Btrfs Info"
msgstr "Btrfs 信息"
msgid "The source path for create the snapshot"
msgstr "创建快照的源数据目录"
msgid "The path where you want to store the snapshot"
msgstr "存放快照数据目录"
msgid "Please input Source Path of snapshot, Source Path must start with '/'"
msgstr "请输入快照源路径,源路径必须以'/'开头"
msgid "Please input Subvolume Path, Subvolume must start with '/'"
msgstr "请输入子卷路径,子卷路径必须以'/'开头"
msgid "is in use! please unmount it first!"
msgstr "正在被使用!请先卸载!"
msgid "Partition NOT found!"
msgstr "分区未找到!"
msgid "Filesystem NOT support!"
msgstr "文件系统不支持!"
msgid "Invalid Start Sector!"
msgstr "无效的起始扇区!"
msgid "Invalid End Sector"
msgstr "无效的终止扇区!"
msgid "Partition not exists!"
msgstr "分区不存在!"
msgid "Creation"
msgstr "创建"
msgid "Please select file system!"
msgstr "请选择文件系统!"
msgid "Format partation:"
msgstr "格式化分区:"
msgid "Warnning !! \nTHIS WILL OVERWRITE EXISTING PARTITIONS!! \nModify the partition table?"
msgstr "警告!!\n此操作会覆盖现有分区\n确定修改分区表"

View File

@ -8,6 +8,7 @@ PKG_LICENSE:=AGPL-3.0
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/lisaac/luci-lib-docker.git
PKG_MIRROR_HASH:=2ae79a80856a29e86a77744fb226aec35d1776d922c9bb06d2b602735ebd71d3
PKG_SOURCE_VERSION:=$(PKG_VERSION)
PKG_SOURCE_SUBDIR:=$(PKG_NAME)

View File

@ -1,432 +0,0 @@
require "nixio.util"
require "luci.util"
local jsonc = require "luci.jsonc"
local nixio = require "nixio"
local ltn12 = require "luci.ltn12"
local fs = require "nixio.fs"
local urlencode = luci.util.urlencode or luci.http and luci.http.protocol and luci.http.protocol.urlencode
local json_stringify = jsonc.stringify
local json_parse = jsonc.parse
local chunksource = function(sock, buffer)
buffer = buffer or ""
return function()
local output
local _, endp, count = buffer:find("^([0-9a-fA-F]+);?.-\r\n")
if not count then -- lua ^ only match start of stirngnot start of line
_, endp, count = buffer:find("\r\n([0-9a-fA-F]+);?.-\r\n")
end
while not count do
local newblock, code = sock:recv(1024)
if not newblock then
return nil, code
end
buffer = buffer .. newblock
_, endp, count = buffer:find("^([0-9a-fA-F]+);?.-\r\n")
if not count then
_, endp, count = buffer:find("\r\n([0-9a-fA-F]+);?.-\r\n")
end
end
count = tonumber(count, 16)
if not count then
return nil, -1, "invalid encoding"
elseif count == 0 then -- finial
return nil
elseif count + 2 <= #buffer - endp then
output = buffer:sub(endp + 1, endp + count)
buffer = buffer:sub(endp + count + 3) -- don't forget handle buffer
return output
else
output = buffer:sub(endp + 1, endp + count)
buffer = ""
if count > #output then
local remain, code = sock:recvall(count - #output) --need read remaining
if not remain then
return nil, code
end
output = output .. remain
count, code = sock:recvall(2) --read \r\n
else
count, code = sock:recvall(count + 2 - #buffer + endp)
end
if not count then
return nil, code
end
return output
end
end
end
local chunksink = function (sock)
return function(chunk, err)
if not chunk then
return sock:writeall("0\r\n\r\n")
else
return sock:writeall(("%X\r\n%s\r\n"):format(#chunk, tostring(chunk)))
end
end
end
local docker_stream_filter = function(buffer)
buffer = buffer or ""
if #buffer < 8 then
return ""
end
local stream_type = ((string.byte(buffer, 1) == 1) and "stdout") or ((string.byte(buffer, 1) == 2) and "stderr") or ((string.byte(buffer, 1) == 0) and "stdin") or "stream_err"
local valid_length =
tonumber(string.byte(buffer, 5)) * 256 * 256 * 256 + tonumber(string.byte(buffer, 6)) * 256 * 256 + tonumber(string.byte(buffer, 7)) * 256 + tonumber(string.byte(buffer, 8))
if valid_length > #buffer + 8 then
return ""
end
return stream_type .. ": " .. string.sub(buffer, 9, valid_length + 8)
-- return string.sub(buffer, 9, valid_length + 8)
end
local send_http_socket = function(socket_path, req_header, req_body, callback)
local docker_socket = nixio.socket("unix", "stream")
if docker_socket:connect(socket_path) ~= true then
return {
headers = {code=497, message="bad socket path", protocol="HTTP/1.1"},
body = {message="can\'t connect to unix socket"}
}
end
if docker_socket:send(req_header) == 0 then
return {
headers={code=498,message="bad socket path", protocol="HTTP/1.1"},
body={message="can\'t send data to unix socket"}
}
end
if req_body and type(req_body) == "function" and req_header and req_header:match("chunked") then
-- chunked send
req_body(chunksink(docker_socket))
elseif req_body and type(req_body) == "function" then
-- normal send by req_body function
req_body(docker_socket)
elseif req_body and type(req_body) == "table" then
-- json
docker_socket:send(json_stringify(req_body))
elseif req_body then
docker_socket:send(req_body)
end
local linesrc = docker_socket:linesource()
-- read socket using source http://w3.impa.br/~diego/software/luasocket/ltn12.html
--http://lua-users.org/wiki/FiltersSourcesAndSinks
-- handle response header
local line = linesrc()
if not line then
docker_socket:close()
return {
headers = {code=499, message="bad socket path", protocol="HTTP/1.1"},
body = {message="no data receive from socket"}
}
end
local response = {code = 0, headers = {}, body = {}}
local p, code, msg = line:match("^([%w./]+) ([0-9]+) (.*)")
response.protocol = p
response.code = tonumber(code)
response.message = msg
line = linesrc()
while line and line ~= "" do
local key, val = line:match("^([%w-]+)%s?:%s?(.*)")
if key and key ~= "Status" then
if type(response.headers[key]) == "string" then
response.headers[key] = {response.headers[key], val}
elseif type(response.headers[key]) == "table" then
response.headers[key][#response.headers[key] + 1] = val
else
response.headers[key] = val
end
end
line = linesrc()
end
-- handle response body
local body_buffer = linesrc(true)
response.body = {}
if type(callback) ~= "function" then
if response.headers["Transfer-Encoding"] == "chunked" then
local source = chunksource(docker_socket, body_buffer)
code = ltn12.pump.all(source, (ltn12.sink.table(response.body))) and response.code or 555
response.code = code
else
local body_source = ltn12.source.cat(ltn12.source.string(body_buffer), docker_socket:blocksource())
code = ltn12.pump.all(body_source, (ltn12.sink.table(response.body))) and response.code or 555
response.code = code
end
else
if response.headers["Transfer-Encoding"] == "chunked" then
local source = chunksource(docker_socket, body_buffer)
callback(response, source)
else
local body_source = ltn12.source.cat(ltn12.source.string(body_buffer), docker_socket:blocksource())
callback(response, body_source)
end
end
docker_socket:close()
return response
end
local gen_header = function(options, http_method, api_group, api_action, name_or_id, request)
local header, query, path
options = options or {}
options.protocol = options.protocol or "HTTP/1.1"
name_or_id = name_or_id ~= "" and name_or_id or nil
if request and type(request.query) == "table" then
local k, v
for k, v in pairs(request.query) do
if type(v) == "table" then
query = (query and query .. "&" or "?") .. k .. "=" .. urlencode(json_stringify(v))
elseif type(v) == "boolean" then
query = (query and query .. "&" or "?") .. k .. "=" .. (v and "true" or "false")
elseif type(v) == "number" or type(v) == "string" then
query = (query and query .. "&" or "?") .. k .. "=" .. v
end
end
end
path = (api_group and ("/" .. api_group) or "") .. (name_or_id and ("/" .. name_or_id) or "") .. (api_action and ("/" .. api_action) or "") .. (query or "")
header = (http_method or "GET") .. " " .. path .. " " .. options.protocol .. "\r\n"
header = header .. "Host: " .. options.host .. "\r\n"
header = header .. "User-Agent: " .. options.user_agent .. "\r\n"
header = header .. "Connection: close\r\n"
if request and type(request.header) == "table" then
local k, v
for k, v in pairs(request.header) do
header = header .. k .. ": " .. v .. "\r\n"
end
end
-- when requst_body is function, we need to custom header using custom header
if request and request.body and type(request.body) == "function" then
if not header:match("Content-Length:") then
header = header .. "Transfer-Encoding: chunked\r\n"
end
elseif http_method == "POST" and request and request.body and type(request.body) == "table" then
local conetnt_json = json_stringify(request.body)
header = header .. "Content-Type: application/json\r\n"
header = header .. "Content-Length: " .. #conetnt_json .. "\r\n"
elseif request and request.body and type(request.body) == "string" then
header = header .. "Content-Length: " .. #request.body .. "\r\n"
end
header = header .. "\r\n"
if options.debug then io.popen("echo '".. header .. "' >> " .. options.debug_path) end
return header
end
local call_docker = function(options, http_method, api_group, api_action, name_or_id, request, callback)
local req_options = setmetatable({}, {__index = options})
local req_header = gen_header(req_options, http_method, api_group, api_action, name_or_id, request)
local req_body = request and request.body or nil
return send_http_socket(req_options.socket_path, req_header, req_body, callback)
end
local gen_api = function(_table, http_method, api_group, api_action)
local _api_action
if api_action == "get_archive" or api_action == "put_archive" then
_api_action = "archive"
elseif api_action == "df" then
_api_action = "system/df"
elseif api_action ~= "list" and api_action ~= "inspect" and api_action ~= "remove" then
_api_action = api_action
elseif (api_group == "containers" or api_group == "images" or api_group == "exec") and (api_action == "list" or api_action == "inspect") then
_api_action = "json"
end
local fp = function(self, request, callback)
local name_or_id = request and (request.name or request.id or request.name_or_id) or nil
if api_action == "list" then
if (name_or_id ~= "" and name_or_id ~= nil) then
if api_group == "images" then
name_or_id = nil
else
request.query = request and request.query or {}
request.query.filters = request.query.filters or {}
request.query.filters.name = request.query.filters.name or {}
request.query.filters.name[#request.query.filters.name + 1] = name_or_id
name_or_id = nil
end
end
elseif api_action == "create" then
if (name_or_id ~= "" and name_or_id ~= nil) then
request.query = request and request.query or {}
request.query.name = request.query.name or name_or_id
name_or_id = nil
end
elseif api_action == "logs" then
local body_buffer = ""
local response = call_docker(self.options, http_method, api_group, _api_action, name_or_id, request, callback)
if response.code >= 200 and response.code < 300 then
for i, v in ipairs(response.body) do
body_buffer = body_buffer .. docker_stream_filter(response.body[i])
end
response.body = body_buffer
end
return response
end
local response = call_docker(self.options, http_method, api_group, _api_action, name_or_id, request, callback)
if response.headers and response.headers["Content-Type"] == "application/json" then
if #response.body == 1 then
response.body = json_parse(response.body[1])
else
local tmp = {}
for _, v in ipairs(response.body) do
tmp[#tmp+1] = json_parse(v)
end
response.body = tmp
end
end
return response
end
if api_group then
_table[api_group][api_action] = fp
else
_table[api_action] = fp
end
end
local _docker = {containers = {}, exec = {}, images = {}, networks = {}, volumes = {}}
gen_api(_docker, "GET", "containers", "list")
gen_api(_docker, "POST", "containers", "create")
gen_api(_docker, "GET", "containers", "inspect")
gen_api(_docker, "GET", "containers", "top")
gen_api(_docker, "GET", "containers", "logs")
gen_api(_docker, "GET", "containers", "changes")
gen_api(_docker, "GET", "containers", "stats")
gen_api(_docker, "POST", "containers", "resize")
gen_api(_docker, "POST", "containers", "start")
gen_api(_docker, "POST", "containers", "stop")
gen_api(_docker, "POST", "containers", "restart")
gen_api(_docker, "POST", "containers", "kill")
gen_api(_docker, "POST", "containers", "update")
gen_api(_docker, "POST", "containers", "rename")
gen_api(_docker, "POST", "containers", "pause")
gen_api(_docker, "POST", "containers", "unpause")
gen_api(_docker, "POST", "containers", "update")
gen_api(_docker, "DELETE", "containers", "remove")
gen_api(_docker, "POST", "containers", "prune")
gen_api(_docker, "POST", "containers", "exec")
gen_api(_docker, "POST", "exec", "start")
gen_api(_docker, "POST", "exec", "resize")
gen_api(_docker, "GET", "exec", "inspect")
gen_api(_docker, "GET", "containers", "get_archive")
gen_api(_docker, "PUT", "containers", "put_archive")
-- TODO: export,attch
gen_api(_docker, "GET", "images", "list")
gen_api(_docker, "POST", "images", "create")
gen_api(_docker, "GET", "images", "inspect")
gen_api(_docker, "GET", "images", "history")
gen_api(_docker, "POST", "images", "tag")
gen_api(_docker, "DELETE", "images", "remove")
gen_api(_docker, "GET", "images", "search")
gen_api(_docker, "POST", "images", "prune")
-- TODO: build clear push commit export import
gen_api(_docker, "GET", "networks", "list")
gen_api(_docker, "GET", "networks", "inspect")
gen_api(_docker, "DELETE", "networks", "remove")
gen_api(_docker, "POST", "networks", "create")
gen_api(_docker, "POST", "networks", "connect")
gen_api(_docker, "POST", "networks", "disconnect")
gen_api(_docker, "POST", "networks", "prune")
gen_api(_docker, "GET", "volumes", "list")
gen_api(_docker, "GET", "volumes", "inspect")
gen_api(_docker, "DELETE", "volumes", "remove")
gen_api(_docker, "POST", "volumes", "create")
gen_api(_docker, "GET", nil, "events")
gen_api(_docker, "GET", nil, "version")
gen_api(_docker, "GET", nil, "info")
gen_api(_docker, "GET", nil, "_ping")
gen_api(_docker, "GET", nil, "df")
function _docker.new(options)
local docker = {}
local _options = options or {}
docker.options = {
socket_path = _options.socket_path or "/var/run/docker.sock",
host = _options.host or "localhost",
version = _options.version or "v1.40",
user_agent = _options.user_agent or "LuCI",
protocol = _options.protocol or "HTTP/1.1",
-- status_enabled = _options.status_enabled or true,
-- status_path = _options.status_path or "/tmp/.docker_action_status",
debug = _options.debug or false,
debug_path = _options.debug_path or "/tmp/.docker_debug"
}
setmetatable(
docker,
{
__index = function(t, key)
if _docker[key] ~= nil then
return _docker[key]
else
return _docker.containers[key]
end
end
}
)
setmetatable(
docker.containers,
{
__index = function(t, key)
if key == "options" then
return docker.options
end
end
}
)
setmetatable(
docker.networks,
{
__index = function(t, key)
if key == "options" then
return docker.options
end
end
}
)
setmetatable(
docker.images,
{
__index = function(t, key)
if key == "options" then
return docker.options
end
end
}
)
setmetatable(
docker.volumes,
{
__index = function(t, key)
if key == "options" then
return docker.options
end
end
}
)
setmetatable(
docker.exec,
{
__index = function(t, key)
if key == "options" then
return docker.options
end
end
}
)
return docker
end
return _docker

View File

@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk
LUCI_TITLE:=Edge Theme
LUCI_DEPENDS:=
LUCI_DEPENDS:=+curl
PKG_VERSION:=2.4
PKG_RELEASE:=20201128

View File

@ -120,7 +120,7 @@ if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent)) {
<%+footer%>
<%
luci.sys.exec("(sleep 1;curl -k -L https://source.unsplash.com/featured/1080x720/?wallpapers -o /www/luci-static/edge/background/1.jpg) &")
luci.sys.exec("(sleep 1;curl -k -L https://source.unsplash.com/featured/1080x720/?wallpaper -o /www/luci-static/edge/background/1.jpg) &")
luci.sys.exec("(sleep 1;curl -k -L https://source.unsplash.com/featured/1080x720/?fashion -o /www/luci-static/edge/background/2.jpg) &")
luci.sys.exec("(sleep 1;curl -k -L https://source.unsplash.com/featured/1080x720/?nature -o /www/luci-static/edge/background/3.jpg) &")
%>

View File

@ -13,6 +13,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/KyleRicardo/MentoHUST-OpenWrt-ipk.git
PKG_MIRROR_HASH:=ac39a84247b2b976430b39a29667d136611252afbb32ac16d4f21831ce07c871
PKG_REV:=557cffca8032b6d8ac948be8a79255dc64a1915d
PKG_SOURCE_VERSION:=$(PKG_REV)

View File

@ -19,6 +19,7 @@ PKG_LICENSE_FILES:=COPYING
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/Mleaf/mwol.git
PKG_MIRROR_HASH:=7684645b84abc1339d84bc0b87dd72ca99c501d1c77b10f7bd5379e6b49833e9
PKG_SOURCE_VERSION:=59f9805901b4ac2916a0273ffbc29197fcd17a62
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz

View File

@ -6,6 +6,7 @@ PKG_VERSION:=3.0
PKG_RELEASE:=2
PKG_SOURCE_URL:=https://github.com/destan19/OpenAppFilter.git
PKG_MIRROR_HASH:=d8d71c8dcdd6e130d2e1b4cddf37781fb95c5d1b8e7090fe49c0e761490be324
PKG_SOURCE_PROTO:=git
PKG_SOURCE_DATE:=2020-10-20
PKG_SOURCE_VERSION:=b235da54c45ac4cd16ec96e1ec832d3851a247da

View File

@ -9,6 +9,7 @@ PKG_BUILD_PARALLEL:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/zfl9/chinadns-ng.git
PKG_MIRROR_HASH:=52b31de1a0790d9b0c8a0118152c4b1a44013a8cfdeed16e5437052744886607
PKG_SOURCE_VERSION:=748a043dd7fb7ec71efbb7306f9dd21db5ce560f
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz

View File

@ -13,6 +13,7 @@ PKG_RELEASE:=5
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/shadowsocks/simple-obfs.git
PKG_MIRROR_HASH:=ea8f2b9825bbb87d5d860524e29bade265141687338db2dbf7ecd32690cf02fc
PKG_SOURCE_VERSION:=486bebd9208539058e57e23a12f23103016e09b4
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz

View File

@ -13,7 +13,7 @@ PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/parted-$(PKG_VERSION)
PKG_SOURCE:=parted-$(PKG_VERSION).tar.xz
PKG_SOURCE_URL:=@GNU/parted
PKG_MD5SUM:=090655d05f3c471aa8e15a27536889ec
PKG_HASH:=57e2b4bd87018625c515421d4524f6e3b55175b472302056391c5f7eccb83d44
include $(INCLUDE_DIR)/package.mk
define Package/parted

View File

@ -6,6 +6,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/boypt/simple-torrent.git
PKG_MIRROR_HASH:=2eefc27ca66e2e2bad9950cd6059b5b4f25f3565319e22ac6b8d00e7cbd27938
PKG_SOURCE_VERSION:=7d2662c35ac0a566b39b56c0d42de81067cc1670
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)

View File

@ -13,6 +13,7 @@ PKG_RELEASE:=2
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/tostercx/ssocks.git
PKG_MIRROR_HASH:=f9447b08d99ffeb2cac3eba1a7b07f5397935db7b7fa4a4e404fb695f13b76f9
PKG_SOURCE_VERSION:=8d9a5d2f89e507c9ffebd838b3c7430f07a0f36c
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz

View File

@ -12,6 +12,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/lzdnico/subweb.git
PKG_MIRROR_HASH:=61855d9412056f2f0eb588840bb35b9b47dea97db887e47c15c52fe897f8b7bd
PKG_SOURCE_VERSION:=9acc47d2e9cda1b16d7fe184a16f528848467810
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz

View File

@ -17,7 +17,7 @@ PKG_SOURCE_DATE:=2020-12-18
PKG_SOURCE_VERSION:=fca4e548391e8198d0a651a3f22af1ceee966d5c
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
PKG_MIRROR_HASH:=510999ec95faef0f8d9d6e5d5f7813b10cc2432c1c859e342a7deabe8de3af2b
PKG_MIRROR_HASH:=9462c77b95fee8f09391b3d88b196ec0232242486b181bb45e366963ae6bd7e5
PKG_LICENSE:=MIT
PKG_MAINTAINER:=ElonH <elonhhuang@gmail.com>

View File

@ -6,6 +6,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/cnsilvan/UnblockNeteaseMusic.git
PKG_MIRROR_HASH:=d513c958d88452b9a521b9b0f896d1d7e2e6089b942aab3307ba4d63ab724598
PKG_SOURCE_VERSION:=935a28125fc7eafaa8273a77352e6262b18efd5e
PKG_MAINTAINER:=Silvan <cnsilvan@gmail.com>

View File

@ -15,6 +15,7 @@ PKG_LICENSE:=MIT
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/nondanee/UnblockNeteaseMusic.git
PKG_MIRROR_HASH:=51422014ed1dd196a12950ac0db2bf88bae6516637440655d0899fac474fbf30
PKG_SOURCE_VERSION:=1193e29a2c8f72c738d2988d5cf5afbb2fee7463
PKG_SOURCE_SUBDIR:=$(PKG_NAME)

View File

@ -18,6 +18,7 @@ PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=$(PKG_REV)
PKG_SOURCE_URL:=https://github.com/amule-project/amule.git
PKG_MIRROR_HASH:=7b1352dd50764844205610173091db146bd0aa02d215278742bc83138ab0852f
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
PKG_BUILD_DEPENDS:=libgd libcryptopp

View File

@ -1,28 +0,0 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=antfs-mount
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define Package/antfs-mount
SECTION:=utils
CATEGORY:=Utilities
SUBMENU:=Filesystem
TITLE:=NTFS mount script for AVM NTFS driver
PKGARCH:=all
DEPENDS:=+kmod-fs-antfs
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/antfs-mount/install
$(INSTALL_DIR) $(1)/sbin
$(INSTALL_BIN) ./files/mount.ntfs $(1)/sbin
endef
$(eval $(call BuildPackage,antfs-mount))

View File

@ -1,2 +0,0 @@
#!/bin/sh
mount -t antfs $@

View File

@ -1,43 +0,0 @@
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=antfs
PKG_RELEASE:=2
PKG_SOURCE_URL:=https://github.com/klukonin/antfs.git
PKG_SOURCE_PROTO:=git
PKG_SOURCE_DATE:=2020-02-10
PKG_SOURCE_VERSION:=b41ba529f6b64b429527e09a06ce0326d5456c05
PKG_LICENSE:=GPL-2.0-only
PKG_LICENSE_FILES:=LICENSE
include $(INCLUDE_DIR)/package.mk
define KernelPackage/fs-antfs
SUBMENU:=Filesystems
TITLE:=AVM NTFS Read/Write Driver
FILES:=$(PKG_BUILD_DIR)/antfs.ko
AUTOLOAD:=$(call AutoLoad,30,antfs,1)
DEPENDS:=+kmod-nls-base
endef
define KernelPackage/fs-antfs/description
Kernel module for NTFS Filesytem
endef
MAKE_OPTS:= \
ARCH="$(LINUX_KARCH)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
M="$(PKG_BUILD_DIR)"
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
$(MAKE_OPTS) \
CONFIG_ANTFS_FS=m \
CONFIG_ANTFS_SYMLINKS=y \
ANTFS_VERSION=07.19-$(call version_abbrev,$(PKG_SOURCE_VERSION)) \
modules
endef
$(eval $(call KernelPackage,fs-antfs))

View File

@ -1,49 +0,0 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=dns2socks
PKG_VERSION:=2.1
PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/kongfl888/dns2socks.git
PKG_SOURCE_VERSION:=1723dfaf96fcd5d9a392e9122a08b92a1248f88b
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
PKG_LICENSE:=GPL-3.0
PKG_MAINTAINER:=kongfl888 <kongfl888@outlook.com>
PKG_USE_MIPS16:=0
PKG_BUILD_PARALLEL:=1
include $(INCLUDE_DIR)/package.mk
define Package/dns2socks
SECTION:=net
CATEGORY:=Network
SUBMENU:=IP Addresses and Names
TITLE:=The utility to resolve DNS requests via a SOCKS5 tunnel.
URL:=https://sourceforge.net/projects/dns2socks/
DEPENDS:=+libpthread
endef
define Package/dns2socks/description
This is a utility to resolve DNS requests via a SOCKS5 tunnel and caches the answers.
endef
define Build/Compile
$(TARGET_CC) \
$(TARGET_CFLAGS) \
$(TARGET_CPPFLAGS) \
$(FPIC) \
-o $(PKG_BUILD_DIR)/DNS2SOCKS/$(PKG_NAME) \
$(PKG_BUILD_DIR)/DNS2SOCKS/DNS2SOCKS.c \
$(TARGET_LDFLAGS) -pthread
endef
define Package/dns2socks/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/DNS2SOCKS/$(PKG_NAME) $(1)/usr/bin/dns2socks
endef
$(eval $(call BuildPackage,dns2socks))

View File

@ -6,6 +6,7 @@ PKG_RELEASE:=10
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/1715173329/dnsforwarder.git
PKG_MIRROR_HASH:=d54eab3778475408b4b615fb5cd13d69b6e7ef1d795fc434427e43ede8ca7128
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_VERSION:=587e61ae4d75dc976f538088b715a3c8ee26c144

View File

@ -13,6 +13,7 @@ PKG_RELEASE=$(PKG_SOURCE_VERSION)
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/hzeller/gmrender-resurrect.git
PKG_MIRROR_HASH:=beed710b377845665e90c20ea734ed9fbb9d2105321b43b7d12dc5a142e4f392
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_VERSION:=26d8f7edf5336bc30f7334c6ad459bf7f4f90ff2
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz

View File

@ -10,7 +10,7 @@ LUCI_DEPENDS:=+adbyby +wget +ipset +dnsmasq-full
LUCI_PKGARCH:=all
PKG_NAME:=luci-app-adbyby-plus
PKG_VERSION:=2.0
PKG_RELEASE:=72
PKG_RELEASE:=73
include $(TOPDIR)/feeds/luci/luci.mk

View File

@ -22,8 +22,8 @@ o.title = translate("Block Apple iOS OTA update")
o.default = 0
o.rmempty = false
o = s:option(Flag, "block_douyin")
o.title = translate("Block Douyin APP and Website")
o = s:option(Flag, "block_cnshort")
o.title = translate("Block CNshort APP and Website")
o.default = 0
o.rmempty = false

View File

@ -125,8 +125,8 @@ msgstr "手动更新规则"
msgid "Block Apple iOS OTA update"
msgstr "拦截 Apple iOS 的OTA更新"
msgid "Block Douyin APP and Website"
msgstr "拦截 抖音 APP 和网站"
msgid "Block CNshort APP and Website"
msgstr "拦截 短视频 APP 和网站"
msgid "RAM Running Mode"
msgstr "内存运行模式"

View File

@ -36,7 +36,7 @@ get_config()
config_get wan_mode $1 wan_mode 1
config_get_bool block_ios $1 block_ios 0
config_get_bool mem_mode $1 mem_mode 1
config_get_bool block_douyin $1 block_douyin 0
config_get_bool block_cnshort $1 block_cnshort 0
}
add_rules()
@ -85,8 +85,6 @@ ip_rule()
esac
done
case $wan_mode in
0)
;;
@ -134,11 +132,34 @@ add_dns()
echo 'conf-dir=/tmp/adbyby/rules/data' >> /tmp/dnsmasq.d/dnsmasq-adbyby.conf
[ $block_ios -eq 1 ] && echo 'address=/mesu.apple.com/0.0.0.0' >> /tmp/dnsmasq.d/dnsmasq-adbyby.conf
if [ $block_douyin -eq 1 ]; then
cat <<-EOF >/tmp/etc/dnsmasq-adbyby.d/08-dnsmasq.douyin
if [ $block_cnshort -eq 1 ]; then
cat <<-EOF >/tmp/etc/dnsmasq-adbyby.d/08-dnsmasq.cnshort
address=/api.amemv.com/0.0.0.0
address=/.amemv.com/0.0.0.0
address=/.tiktokv.com/0.0.0.0
address=/.snssdk.com/0.0.0.0
address=/.douyin.com/0.0.0.0
address=/.ixigua.com/0.0.0.0
address=/.pstatp.com/0.0.0.0
address=/.ixiguavideo.com/0.0.0.0
address=/.v.kandian.qq.com/0.0.0.0
address=/.yximgs.com/0.0.0.0
address=/.gifshow.com/0.0.0.0
address=/.ksapisrv.com/0.0.0.0
address=/.kuaishoupay.com/0.0.0.0
address=/.ksyun.com/0.0.0.0
address=/.live.xycdn.com/0.0.0.0
address=/.danuoyi.alicdn.com/0.0.0.0
address=/.v.weishi.qq.com/0.0.0.0
address=/.pearvideo.com/0.0.0.0
address=/.miaopai.com/0.0.0.0
address=/.kuaishou.com/0.0.0.0
address=/.qupai.me/0.0.0.0
address=/.meipai.com/0.0.0.0
address=/.huoshan.com/0.0.0.0
address=/.ergengtv.com/0.0.0.0
address=/.baijiahao.baidu.com/0.0.0.0
address=/.xiongzhang.baidu.com/0.0.0.0
EOF
fi
}

View File

View File

View File

@ -1,14 +1,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-ssr-plus
PKG_VERSION:=181
PKG_RELEASE:=2
include $(INCLUDE_DIR)/package.mk
PKG_VERSION:=182
PKG_RELEASE:=1
define Package/$(PKG_NAME)/conffiles
/etc/config/shadowsocksr
/etc/ssr/
/etc/ssrplus/
endef
define Package/$(PKG_NAME)/config
@ -56,7 +54,7 @@ PKG_CONFIG_DEPENDS:= \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Kcptun \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Server
LUCI_TITLE:=SS/SSR/V2Ray/Xray/Trojan/NaiveProxy/Socks5/Tun LuCI interface
LUCI_TITLE:=SS/SSR/V2Ray/Trojan/NaiveProxy/Socks5/Tun LuCI interface
LUCI_PKGARCH:=all
LUCI_DEPENDS:=+shadowsocksr-libev-alt +ipset +ip-full +iptables-mod-tproxy +dnsmasq-full +coreutils +coreutils-base64 +pdnsd-alt +wget +lua +libuci-lua \
+microsocks +dns2socks +shadowsocks-libev-ss-local +shadowsocksr-libev-ssr-local +shadowsocks-libev-ss-redir +simple-obfs +tcping +resolveip\

View File

@ -1,30 +1,30 @@
-- Copyright (C) 2017 yushi studio <ywb94@qq.com>
-- Licensed to the public under the GNU General Public License v3.
module("luci.controller.shadowsocksr", package.seeall)
function index()
if not nixio.fs.access("/etc/config/shadowsocksr") then
return
end
entry({"admin", "services", "shadowsocksr"}, alias("admin", "services", "shadowsocksr", "client"),_("ShadowSocksR Plus+"), 10).dependent = true
entry({"admin", "services", "shadowsocksr", "client"}, cbi("shadowsocksr/client"),_("SSR Client"), 10).leaf = true
entry({"admin", "services", "shadowsocksr", "servers"}, arcombine(cbi("shadowsocksr/servers", {autoapply = true}), cbi("shadowsocksr/client-config")),_("Severs Nodes"), 20).leaf = true
entry({"admin", "services", "shadowsocksr", "control"},cbi("shadowsocksr/control"), _("Access Control"), 30).leaf = true
entry({"admin", "services", "shadowsocksr", "advanced"},cbi("shadowsocksr/advanced"),_("Advanced Settings"), 50).leaf = true
entry({"admin", "services", "shadowsocksr", "server"},arcombine(cbi("shadowsocksr/server"), cbi("shadowsocksr/server-config")),_("SSR Server"), 60).leaf = true
entry({"admin", "services", "shadowsocksr", "status"},form("shadowsocksr/status"),_("Status"), 70).leaf = true
entry({"admin", "services", "shadowsocksr"}, alias("admin", "services", "shadowsocksr", "client"), _("ShadowSocksR Plus+"), 10).dependent = true
entry({"admin", "services", "shadowsocksr", "client"}, cbi("shadowsocksr/client"), _("SSR Client"), 10).leaf = true
entry({"admin", "services", "shadowsocksr", "servers"}, arcombine(cbi("shadowsocksr/servers", {autoapply = true}), cbi("shadowsocksr/client-config")), _("Severs Nodes"), 20).leaf = true
entry({"admin", "services", "shadowsocksr", "control"}, cbi("shadowsocksr/control"), _("Access Control"), 30).leaf = true
entry({"admin", "services", "shadowsocksr", "advanced"}, cbi("shadowsocksr/advanced"), _("Advanced Settings"), 50).leaf = true
entry({"admin", "services", "shadowsocksr", "server"}, arcombine(cbi("shadowsocksr/server"), cbi("shadowsocksr/server-config")), _("SSR Server"), 60).leaf = true
entry({"admin", "services", "shadowsocksr", "status"}, form("shadowsocksr/status"), _("Status"), 70).leaf = true
entry({"admin", "services", "shadowsocksr", "check"}, call("check_status"))
entry({"admin", "services", "shadowsocksr", "refresh"}, call("refresh_data"))
entry({"admin", "services", "shadowsocksr", "subscribe"}, call("subscribe"))
entry({"admin", "services", "shadowsocksr", "checkport"}, call("check_port"))
entry({"admin", "services", "shadowsocksr", "log"},form("shadowsocksr/log"),_("Log"), 80).leaf = true
entry({"admin", "services", "shadowsocksr","run"},call("act_status")).leaf = true
entry({"admin", "services", "shadowsocksr", "log"}, form("shadowsocksr/log"), _("Log"), 80).leaf = true
entry({"admin", "services", "shadowsocksr", "run"}, call("act_status")).leaf = true
entry({"admin", "services", "shadowsocksr", "ping"}, call("act_ping")).leaf = true
entry({"admin", "services", "shadowsocksr", "reset"}, call("act_reset"))
end
function subscribe()
luci.sys.call("/usr/bin/lua /usr/share/shadowsocksr/subscribe.lua >> /tmp/ssrplus.log 2>&1")
luci.sys.call("/usr/bin/lua /usr/share/shadowsocksr/subscribe.lua >>/var/log/ssrplus.log")
luci.http.prepare_content("application/json")
luci.http.write_json({ret = 1})
end
@ -41,16 +41,16 @@ function act_ping()
local domain = luci.http.formvalue("domain")
local port = luci.http.formvalue("port")
e.index = luci.http.formvalue("index")
local iret = luci.sys.call(" ipset add ss_spec_wan_ac " .. domain .. " 2>/dev/null")
local iret = luci.sys.call("ipset add ss_spec_wan_ac " .. domain .. " 2>/dev/null")
local socket = nixio.socket("inet", "stream")
socket:setopt("socket", "rcvtimeo", 3)
socket:setopt("socket", "sndtimeo", 3)
e.socket = socket:connect(domain, port)
socket:close()
e.ping = luci.sys.exec("ping -c 1 -W 1 %q 2>&1 | grep -o 'time=[0-9]*.[0-9]' | awk -F '=' '{print$2}'" % domain)
if (e.ping == "") then
e.ping = luci.sys.exec(string.format("echo -n $(tcping -q -c 1 -i 1 -t 2 -p %s %s 2>&1 | grep -o 'time=[0-9]*' | awk -F '=' '{print $2}') 2>/dev/null",port, domain))
end
-- e.ping = luci.sys.exec("ping -c 1 -W 1 %q 2>&1 | grep -o 'time=[0-9]*.[0-9]' | awk -F '=' '{print$2}'" % domain)
-- if (e.ping == "") then
e.ping = luci.sys.exec(string.format("echo -n $(tcping -q -c 1 -i 1 -t 2 -p %s %s 2>&1 | grep -o 'time=[0-9]*' | awk -F '=' '{print $2}') 2>/dev/null", port, domain))
-- end
if (iret == 0) then
luci.sys.call(" ipset del ss_spec_wan_ac " .. domain)
end
@ -72,55 +72,9 @@ end
function refresh_data()
local set = luci.http.formvalue("set")
local uci = luci.model.uci.cursor()
local icount = 0
local retstring = 0
local function update(url, file, type, file2)
local Num = 1
refresh_cmd = "wget-ssl --no-check-certificate -t 3 -T 10 -O- " .. url .. " > /tmp/ssr-update." .. type
sret = luci.sys.call(refresh_cmd .. " 2>/dev/null")
if sret == 0 then
if type == "gfw_data" then
luci.sys.call("/usr/bin/ssr-gfw " .. type)
Num = 2
end
if type == "ad_data" then
luci.sys.call("/usr/bin/ssr-ad " .. type)
end
local new_md5 = luci.sys.exec("echo -n $([ -f '/tmp/ssr-update." .. type .. "' ] && md5sum /tmp/ssr-update." .. type .. " | awk '{print $1}')")
local old_md5 = luci.sys.exec("echo -n $([ -f '" .. file .. "' ] && md5sum " .. file .. " | awk '{print $1}')")
if new_md5 == old_md5 then
retstring = "0"
else
icount = luci.sys.exec("cat /tmp/ssr-update." .. type .. " | wc -l")
luci.sys.exec("cp -f /tmp/ssr-update." .. type .. " " .. file)
if file2 then luci.sys.exec("cp -f /tmp/ssr-update." .. type .. " " .. file2) end
retstring = tostring(tonumber(icount)/Num)
if type == "gfw_data" or type == "ad_data" then
luci.sys.exec("/usr/share/shadowsocksr/gfw2ipset.sh")
else
luci.sys.exec("/usr/share/shadowsocksr/chinaipset.sh /tmp/etc/china_ssr.txt")
end
end
else
retstring = "-1"
end
luci.sys.exec("rm -f /tmp/ssr-update." .. type)
end
if set == "gfw_data" then
update(uci:get_first("shadowsocksr", "global", "gfwlist_url", "https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt"), "/etc/ssr/gfw_list.conf", set, "/tmp/dnsmasq.ssr/gfw_list.conf")
end
if set == "ip_data" then
update(uci:get_first("shadowsocksr", "global", "chnroute_url","https://ispip.clang.cn/all_cn.txt"), "/etc/ssr/china_ssr.txt", set, "/tmp/etc/china_ssr.txt")
end
if set == "ad_data" then
update(uci:get_first("shadowsocksr", "global", "adblock_url","https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt"), "/etc/ssr/ad.conf", set, "/tmp/dnsmasq.ssr/ad.conf")
end
if set == "nfip_data" then
update(uci:get_first("shadowsocksr", "global", "nfip_url","https://raw.githubusercontent.com/QiuSimons/Netflix_IP/master/NF_only.txt"), "/etc/ssr/netflixip.list", set)
end
local retstring = luci.sys.exec("/usr/bin/lua /usr/share/shadowsocksr/update.lua " .. set)
luci.http.prepare_content("application/json")
luci.http.write_json({ret = retstring,retcount = icount})
luci.http.write_json({ret = tonumber(retstring)})
end
function check_port()
@ -134,13 +88,13 @@ function check_port()
if s.alias then
server_name = s.alias
elseif s.server and s.server_port then
server_name = "%s:%s" %{s.server, s.server_port}
server_name = "%s:%s" % {s.server, s.server_port}
end
iret = luci.sys.call("ipset add ss_spec_wan_ac " .. s.server .. " 2>/dev/null")
socket = nixio.socket("inet", "stream")
socket:setopt("socket", "rcvtimeo", 3)
socket:setopt("socket", "sndtimeo", 3)
ret = socket:connect(s.server,s.server_port)
ret = socket:connect(s.server, s.server_port)
if tostring(ret) == "true" then
socket:close()
retstring = retstring .. "<font color = 'green'>[" .. server_name .. "] OK.</font><br />"
@ -154,3 +108,9 @@ function check_port()
luci.http.prepare_content("application/json")
luci.http.write_json({ret = retstring})
end
function act_reset()
nixio.exec("/etc/init.d/shadowsocksr", "reset")
http.redirect(luci.dispatcher.build_url('admin/services/shadowsocksr'))
return
end

View File

@ -1,19 +1,18 @@
local shadowsocksr = "shadowsocksr"
local uci = luci.model.uci.cursor()
local server_table = {}
uci:foreach(shadowsocksr, "servers", function(s)
uci:foreach("shadowsocksr", "servers", function(s)
if s.alias then
server_table[s[".name"]] = "[%s]:%s" %{string.upper(s.type), s.alias}
server_table[s[".name"]] = "[%s]:%s" % {string.upper(s.type), s.alias}
elseif s.server and s.server_port then
server_table[s[".name"]] = "[%s]:%s:%s" %{string.upper(s.type), s.server, s.server_port}
server_table[s[".name"]] = "[%s]:%s:%s" % {string.upper(s.type), s.server, s.server_port}
end
end)
local key_table = {}
for key,_ in pairs(server_table) do
table.insert(key_table,key)
end
local key_table = {}
for key, _ in pairs(server_table) do
table.insert(key_table, key)
end
table.sort(key_table)
@ -49,14 +48,16 @@ o = s:option(Flag, "adblock", translate("Enable adblock"))
o.rmempty = false
o = s:option(Value, "adblock_url", translate("adblock_url"))
o:value("https://neodev.team/lite_host_dnsmasq.conf", translate("NEO DEV HOST Lite"))
o:value("https://neodev.team/host_dnsmasq.conf", translate("NEO DEV HOST Full"))
o:value("https://anti-ad.net/anti-ad-for-dnsmasq.conf", translate("anti-AD"))
o:value("https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt", translate("Easylist China"))
o.default = "https://anti-ad.net/anti-ad-for-dnsmasq.conf"
o.default = "https://neodev.team/lite_host_dnsmasq.conf"
o:depends("adblock", "1")
o.description = translate("Support AdGuardHome and DNSMASQ format list")
o = s:option(Value, "gfwlist_url", translate("gfwlist Update url"))
o:value("https://cdn.jsdelivr.net/gh/v2fly/domain-list-community@release/gfwlist.txt", translate("v2fly/domain-list-community"))
o:value("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt", translate("Loyalsoldier/v2ray-rules-dat"))
o:value("https://cdn.jsdelivr.net/gh/Loukky/gfwlist-by-loukky/gfwlist.txt", translate("Loukky/gfwlist-by-loukky"))
o:value("https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt", translate("gfwlist/gfwlist"))
o.default = "https://cdn.jsdelivr.net/gh/v2fly/domain-list-community@release/gfwlist.txt"
@ -72,6 +73,10 @@ o:value("https://cdn.jsdelivr.net/gh/QiuSimons/Netflix_IP/getflix.txt", translat
o.default = "https://cdn.jsdelivr.net/gh/QiuSimons/Netflix_IP/NF_only.txt"
o.description = translate("Customize Netflix IP Url")
o = s:option(Button, "reset", translate("Reset to defaults"))
o.rawhtml = true
o.template = "shadowsocksr/reset"
-- [[ SOCKS5 Proxy ]]--
s = m:section(TypedSection, "socks5_proxy", translate("Global SOCKS5 Proxy Server"))
s.anonymous = true
@ -79,7 +84,9 @@ s.anonymous = true
o = s:option(ListValue, "server", translate("Server"))
o:value("nil", translate("Disable"))
o:value("same", translate("Same as Global Server"))
for _,key in pairs(key_table) do o:value(key,server_table[key]) end
for _, key in pairs(key_table) do
o:value(key, server_table[key])
end
o.default = "nil"
o.rmempty = false

View File

@ -1,118 +1,121 @@
-- Copyright (C) 2017 yushi studio <ywb94@qq.com> github.com/ywb94
-- Licensed to the public under the GNU General Public License v3.
require "nixio.fs"
require "luci.sys"
require "luci.http"
local m, s, o,kcp_enable
local shadowsocksr = "shadowsocksr"
local m, s, o, kcp_enable
local sid = arg[1]
local uuid = luci.sys.exec("cat /proc/sys/kernel/random/uuid")
local function isKcptun(file)
if not nixio.fs.access(file, "rwx", "rx", "rx") then
nixio.fs.chmod(file, 755)
end
local str = luci.sys.exec(file .. " -v | awk '{printf $1}'")
return (str:lower() == "kcptun")
if not nixio.fs.access(file, "rwx", "rx", "rx") then
nixio.fs.chmod(file, 755)
end
local str = luci.sys.exec(file .. " -v | awk '{printf $1}'")
return (str:lower() == "kcptun")
end
local server_table = {}
local encrypt_methods = {
"none",
"table",
"rc4",
"rc4-md5-6",
"rc4-md5",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"bf-cfb",
"camellia-128-cfb",
"camellia-192-cfb",
"camellia-256-cfb",
"cast5-cfb",
"des-cfb",
"idea-cfb",
"rc2-cfb",
"seed-cfb",
"salsa20",
"chacha20",
"chacha20-ietf",
-- ssr
"none",
"table",
"rc4",
"rc4-md5-6",
"rc4-md5",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"bf-cfb",
"camellia-128-cfb",
"camellia-192-cfb",
"camellia-256-cfb",
"cast5-cfb",
"des-cfb",
"idea-cfb",
"rc2-cfb",
"seed-cfb",
"salsa20",
"chacha20",
"chacha20-ietf"
}
local encrypt_methods_ss = {
-- aead
"aes-128-gcm",
"aes-192-gcm",
"aes-256-gcm",
"chacha20-ietf-poly1305",
"xchacha20-ietf-poly1305",
-- stream
"table",
"rc4",
"rc4-md5",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"bf-cfb",
"camellia-128-cfb",
"camellia-192-cfb",
"camellia-256-cfb",
"salsa20",
"chacha20",
"chacha20-ietf",
-- aead
"aes-128-gcm",
"aes-192-gcm",
"aes-256-gcm",
"chacha20-ietf-poly1305",
"xchacha20-ietf-poly1305",
-- stream
"table",
"rc4",
"rc4-md5",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"bf-cfb",
"camellia-128-cfb",
"camellia-192-cfb",
"camellia-256-cfb",
"salsa20",
"chacha20",
"chacha20-ietf"
}
local protocol = {
"origin",
"verify_deflate",
"auth_sha1_v4",
"auth_aes128_sha1",
"auth_aes128_md5",
"auth_chain_a",
"auth_chain_b",
"auth_chain_c",
"auth_chain_d",
"auth_chain_e",
"auth_chain_f",
-- ssr
"origin",
"verify_deflate",
"auth_sha1_v4",
"auth_aes128_sha1",
"auth_aes128_md5",
"auth_chain_a",
"auth_chain_b",
"auth_chain_c",
"auth_chain_d",
"auth_chain_e",
"auth_chain_f"
}
obfs = {
"plain",
"http_simple",
"http_post",
"random_head",
"tls1.2_ticket_auth",
-- ssr
"plain",
"http_simple",
"http_post",
"random_head",
"tls1.2_ticket_auth"
}
local securitys = {
"auto",
"none",
"aes-128-gcm",
"chacha20-poly1305"
-- vmess
"auto",
"none",
"aes-128-gcm",
"chacha20-poly1305"
}
local flows = {
"xtls-rprx-origin",
"xtls-rprx-origin-udp443",
"xtls-rprx-direct",
"xtls-rprx-direct-udp443",
"xtls-rprx-splice",
"xtls-rprx-splice-udp443"
-- xlts
"xtls-rprx-origin",
"xtls-rprx-origin-udp443",
"xtls-rprx-direct",
"xtls-rprx-direct-udp443",
"xtls-rprx-splice",
"xtls-rprx-splice-udp443"
}
m = Map(shadowsocksr, translate("Edit ShadowSocksR Server"))
m = Map("shadowsocksr", translate("Edit ShadowSocksR Server"))
m.redirect = luci.dispatcher.build_url("admin/services/shadowsocksr/servers")
if m.uci:get(shadowsocksr, sid) ~= "servers" then
luci.http.redirect(m.redirect)
return
if m.uci:get("shadowsocksr", sid) ~= "servers" then
luci.http.redirect(m.redirect)
return
end
-- [[ Servers Setting ]]--
@ -120,29 +123,31 @@ s = m:section(NamedSection, sid, "servers")
s.anonymous = true
s.addremove = false
o = s:option(DummyValue,"ssr_url","SS/SSR/V2RAY/TROJAN URL")
o = s:option(DummyValue, "ssr_url", "SS/SSR/V2RAY/TROJAN URL")
o.rawhtml = true
o.template = "shadowsocksr/ssrurl"
o.value =sid
o.value = sid
o = s:option(ListValue, "type", translate("Server Node Type"))
o:value("ssr", translate("ShadowsocksR"))
if nixio.fs.access("/usr/bin/ss-redir") then
o:value("ss", translate("Shadowsocks New Version"))
if nixio.fs.access("/usr/bin/ssr-redir") then
o:value("ssr", translate("ShadowsocksR"))
end
if nixio.fs.access("/usr/bin/xray") or nixio.fs.access("/usr/bin/xray/xray") or nixio.fs.access("/usr/bin/v2ray/v2ray") or nixio.fs.access("/usr/bin/v2ray") then
o:value("v2ray", translate("V2Ray"))
o:value("vless", translate("VLESS"))
if nixio.fs.access("/usr/bin/ss-redir") then
o:value("ss", translate("Shadowsocks New Version"))
end
if nixio.fs.access("/usr/bin/xray") or nixio.fs.access("/usr/bin/v2ray") then
o:value("vmess", translate("Vmess"))
o:value("vless", translate("VLESS"))
end
if nixio.fs.access("/usr/sbin/trojan") then
o:value("trojan", translate("Trojan"))
o:value("trojan", translate("Trojan"))
end
if nixio.fs.access("/usr/bin/naive") then
o:value("naiveproxy", translate("NaiveProxy"))
o:value("naiveproxy", translate("NaiveProxy"))
end
if nixio.fs.access("/usr/sbin/redsocks2") then
o:value("socks5", translate("Socks5"))
o:value("tun", translate("Network Tunnel"))
o:value("socks5", translate("Socks5"))
o:value("tun", translate("Network Tunnel"))
end
o.description = translate("Using incorrect encryption mothod may causes service fail to start")
@ -150,7 +155,9 @@ o = s:option(Value, "alias", translate("Alias(optional)"))
o = s:option(ListValue, "iface", translate("Network interface to use"))
for _, e in ipairs(luci.sys.net.devices()) do
if e ~= "lo" then o:value(e) end
if e ~= "lo" then
o:value(e)
end
end
o:depends("type", "tun")
o.description = translate("Redirect traffic to this network interface")
@ -160,7 +167,7 @@ o.datatype = "host"
o.rmempty = false
o:depends("type", "ssr")
o:depends("type", "ss")
o:depends("type", "v2ray")
o:depends("type", "vmess")
o:depends("type", "vless")
o:depends("type", "trojan")
o:depends("type", "naiveproxy")
@ -171,7 +178,7 @@ o.datatype = "port"
o.rmempty = false
o:depends("type", "ssr")
o:depends("type", "ss")
o:depends("type", "v2ray")
o:depends("type", "vmess")
o:depends("type", "vless")
o:depends("type", "trojan")
o:depends("type", "naiveproxy")
@ -185,7 +192,7 @@ o:depends("type", "socks5")
o = s:option(Value, "username", translate("Username"))
o.rmempty = true
o:depends("type", "naiveproxy")
o:depends("type", "socks5")
o:depends({type = "socks5", auth_enable = true})
o = s:option(Value, "password", translate("Password"))
o.password = true
@ -194,29 +201,57 @@ o:depends("type", "ssr")
o:depends("type", "ss")
o:depends("type", "trojan")
o:depends("type", "naiveproxy")
o:depends("type", "socks5")
o:depends({type = "socks5", auth_enable = true})
o = s:option(ListValue, "encrypt_method", translate("Encrypt Method"))
for _, v in ipairs(encrypt_methods) do o:value(v) end
for _, v in ipairs(encrypt_methods) do
o:value(v)
end
o.rmempty = true
o:depends("type", "ssr")
o = s:option(ListValue, "encrypt_method_ss", translate("Encrypt Method"))
for _, v in ipairs(encrypt_methods_ss) do o:value(v) end
for _, v in ipairs(encrypt_methods_ss) do
o:value(v)
end
o.rmempty = true
o:depends("type", "ss")
-- Shadowsocks Plugin
o = s:option(Value, "plugin", translate("Plugin"))
o = s:option(ListValue, "plugin", translate("Obfs"))
o:value("", translate("None"))
if nixio.fs.access("/usr/bin/obfs-local") then
o:value("obfs-local", translate("simple-obfs"))
end
if nixio.fs.access("/usr/bin/v2ray-plugin") then
o:value("v2ray-plugin", translate("v2ray-plugin"))
end
o.rmempty = true
o:depends("type", "ss")
o = s:option(Value, "plugin_opts", translate("Plugin Opts"))
o = s:option(ListValue, "simple_obfs", translate("Plugin Opts"))
o:value("obfs=tls;obfs-host=", translate("TLS"))
o:value("obfs=http;obfs-host=", translate("HTTP"))
o:depends("plugin", "obfs-local")
o.rmempty = true
o:depends("type", "ss")
o = s:option(ListValue, "v2ray_plugin", translate("Plugin Opts"))
o:value("tls;host=", translate("TLS"))
o:value("mode=quic;host=", translate("QUIC"))
o:value("none", translate("HTTP"))
o:depends("plugin", "v2ray-plugin")
o.rmempty = true
o = s:option(Value, "plugin_opts", translate("Plugin Opts"), translate("Please fill in the Host, for example: www.baidu.com"))
o.rmempty = true
o:depends({type = "ss", plugin = "obfs-local"})
o:depends({type = "ss", v2ray_plugin = "tls;host="})
o:depends({type = "ss", v2ray_plugin = "mode=quic;host="})
o = s:option(ListValue, "protocol", translate("Protocol"))
for _, v in ipairs(protocol) do o:value(v) end
for _, v in ipairs(protocol) do
o:value(v)
end
o.rmempty = true
o:depends("type", "ssr")
@ -224,7 +259,9 @@ o = s:option(Value, "protocol_param", translate("Protocol param(optional)"))
o:depends("type", "ssr")
o = s:option(ListValue, "obfs", translate("Obfs"))
for _, v in ipairs(obfs) do o:value(v) end
for _, v in ipairs(obfs) do
o:value(v)
end
o.rmempty = true
o:depends("type", "ssr")
@ -236,13 +273,13 @@ o = s:option(Value, "alter_id", translate("AlterId"))
o.datatype = "port"
o.default = 16
o.rmempty = true
o:depends("type", "v2ray")
o:depends("type", "vmess")
-- VmessId
o = s:option(Value, "vmess_id", translate("Vmess/VLESS ID (UUID)"))
o.rmempty = true
o.default = uuid
o:depends("type", "v2ray")
o:depends("type", "vmess")
o:depends("type", "vless")
-- VLESS Encryption
@ -253,9 +290,11 @@ o:depends("type", "vless")
-- 加密方式
o = s:option(ListValue, "security", translate("Encrypt Method"))
for _, v in ipairs(securitys) do o:value(v, v:upper()) end
for _, v in ipairs(securitys) do
o:value(v, v:upper())
end
o.rmempty = true
o:depends("type", "v2ray")
o:depends("type", "vmess")
-- 传输协议
o = s:option(ListValue, "transport", translate("Transport"))
@ -265,11 +304,10 @@ o:value("ws", "WebSocket")
o:value("h2", "HTTP/2")
o:value("quic", "QUIC")
o.rmempty = true
o:depends("type", "v2ray")
o:depends("type", "vmess")
o:depends("type", "vless")
-- [[ TCP部分 ]]--
-- TCP伪装
o = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
o:depends("transport", "tcp")
@ -288,10 +326,9 @@ o:depends("tcp_guise", "http")
o.rmempty = true
-- [[ WS部分 ]]--
-- WS域名
o = s:option(Value, "ws_host", translate("WebSocket Host"))
o:depends("transport", "ws")
o:depends({transport = "ws", tls = false})
o.rmempty = true
-- WS路径
@ -312,7 +349,6 @@ o:depends("transport", "h2")
o.rmempty = true
-- [[ QUIC部分 ]]--
o = s:option(ListValue, "quic_security", translate("QUIC Security"))
o:depends("transport", "quic")
o:value("none", translate("None"))
@ -331,19 +367,18 @@ o:value("none", translate("None"))
o:value("srtp", translate("VideoCall (SRTP)"))
o:value("utp", translate("BitTorrent (uTP)"))
o:value("wechat-video", translate("WechatVideo"))
o:value("dtls", "DTLS 1.2")
o:value("wireguard", "WireGuard")
o:value("dtls", translate("DTLS 1.2"))
o:value("wireguard", translate("WireGuard"))
-- [[ mKCP部分 ]]--
o = s:option(ListValue, "kcp_guise", translate("Camouflage Type"))
o:depends("transport", "kcp")
o:value("none", translate("None"))
o:value("srtp", translate("VideoCall (SRTP)"))
o:value("utp", translate("BitTorrent (uTP)"))
o:value("wechat-video", translate("WechatVideo"))
o:value("dtls", "DTLS 1.2")
o:value("wireguard", "WireGuard")
o:value("dtls", translate("DTLS 1.2"))
o:value("wireguard", translate("WireGuard"))
o.rmempty = true
o = s:option(Value, "mtu", translate("MTU"))
@ -383,55 +418,56 @@ o.default = 2
o.rmempty = true
o = s:option(Value, "seed", translate("Obfuscate password (optional)"))
o:depends({type="vless", transport="kcp"})
o:depends({type = "vless", transport = "kcp"})
o.rmempty = true
o = s:option(Flag, "congestion", translate("Congestion"))
o:depends("transport", "kcp")
o.rmempty = true
-- [[ allowInsecure ]]--
o = s:option(Flag, "insecure", translate("allowInsecure"))
o.rmempty = false
o:depends("type", "v2ray")
o:depends("type", "vless")
o:depends("type", "trojan")
o.default = "0"
o.description = translate("If true, allowss insecure connection at TLS client, e.g., TLS server uses unverifiable certificates.")
-- [[ TLS ]]--
o = s:option(Flag, "tls", translate("TLS"))
o.rmempty = true
o.default = "0"
o:depends("type", "v2ray")
o:depends("type", "vless")
o:depends("type", "vmess")
o:depends({type = "vless", xtls = false})
o:depends("type", "trojan")
o = s:option(Value, "tls_host", translate("TLS Host"))
--o:depends("type", "trojan")
o:depends("tls", "1")
o.rmempty = true
-- XTLS
if nixio.fs.access("/usr/bin/xray") or nixio.fs.access("/usr/bin/xray/xray") then
o = s:option(Flag, "xtls", translate("XTLS"))
o.rmempty = true
o.default = "0"
o:depends({type="vless", tls=true})
if nixio.fs.access("/usr/bin/xray") then
o = s:option(Flag, "xtls", translate("XTLS"))
o.rmempty = true
o.default = "0"
o:depends({type = "vless", transport = "tcp", tls = false})
end
-- Flow
o = s:option(Value, "vless_flow", translate("Flow"))
for _, v in ipairs(flows) do o:value(v, v) end
for _, v in ipairs(flows) do
o:value(v, v)
end
o.rmempty = true
o.default = "xtls-rprx-origin"
o:depends("xtls", "1")
o.default = "xtls-rprx-splice"
o:depends("xtls", true)
o = s:option(Value, "tls_host", translate("TLS Host"))
o:depends("type", "trojan")
o:depends("tls", true)
o:depends("xtls", true)
o.rmempty = true
-- [[ allowInsecure ]]--
o = s:option(Flag, "insecure", translate("allowInsecure"))
o.rmempty = false
o:depends("tls", true)
o:depends("xtls", true)
o.description = translate("If true, allowss insecure connection at TLS client, e.g., TLS server uses unverifiable certificates.")
-- [[ Mux ]]--
o = s:option(Flag, "mux", translate("Mux"))
o.rmempty = true
o.default = "0"
o:depends("type", "v2ray")
o:depends({type="vless", xtls=false})
o.rmempty = false
o:depends("type", "vmess")
o:depends({type = "vless", xtls = false})
o = s:option(Value, "concurrency", translate("Concurrency"))
o.datatype = "uinteger"
@ -444,7 +480,7 @@ o = s:option(Flag, "certificate", translate("Self-signed Certificate"))
o.rmempty = true
o.default = "0"
o:depends("type", "trojan")
o:depends("type", "v2ray")
o:depends("type", "vmess")
o:depends("type", "vless")
o.description = translate("If you have a self-signed certificate,please check the box")
@ -455,31 +491,31 @@ o:depends("certificate", 1)
cert_dir = "/etc/ssl/private/"
local path
luci.http.setfilehandler(
function(meta, chunk, eof)
if not fd then
if (not meta) or (not meta.name) or (not meta.file) then return end
fd = nixio.open(cert_dir .. meta.file, "w")
if not fd then
path = translate("Create upload file error.")
return
end
end
if chunk and fd then
fd:write(chunk)
end
if eof and fd then
fd:close()
fd = nil
path = '/etc/ssl/private/' .. meta.file .. ''
end
end
)
luci.http.setfilehandler(function(meta, chunk, eof)
if not fd then
if (not meta) or (not meta.name) or (not meta.file) then
return
end
fd = nixio.open(cert_dir .. meta.file, "w")
if not fd then
path = translate("Create upload file error.")
return
end
end
if chunk and fd then
fd:write(chunk)
end
if eof and fd then
fd:close()
fd = nil
path = '/etc/ssl/private/' .. meta.file .. ''
end
end)
if luci.http.formvalue("upload") then
local f = luci.http.formvalue("ulfile")
if #f <= 0 then
path = translate("No specify upload file.")
end
local f = luci.http.formvalue("ulfile")
if #f <= 0 then
path = translate("No specify upload file.")
end
end
o = s:option(Value, "certpath", translate("Current Certificate Path"))
@ -505,40 +541,40 @@ o.default = 1234
o.rmempty = false
if nixio.fs.access("/usr/bin/kcptun-client") then
kcp_enable = s:option(Flag, "kcp_enable", translate("KcpTun Enable"), translate("bin:/usr/bin/kcptun-client"))
kcp_enable.rmempty = true
kcp_enable.default = "0"
kcp_enable:depends("type", "ssr")
kcp_enable:depends("type", "ss")
kcp_enable = s:option(Flag, "kcp_enable", translate("KcpTun Enable"), translate("bin:/usr/bin/kcptun-client"))
kcp_enable.rmempty = true
kcp_enable.default = "0"
kcp_enable:depends("type", "ssr")
kcp_enable:depends("type", "ss")
o = s:option(Value, "kcp_port", translate("KcpTun Port"))
o.datatype = "port"
o.default = 4000
function o.validate(self, value, section)
local kcp_file="/usr/bin/kcptun-client"
local enable = kcp_enable:formvalue(section) or kcp_enable.disabled
if enable == kcp_enable.enabled then
if not nixio.fs.access(kcp_file) then
return nil, translate("Haven't a Kcptun executable file")
elseif not isKcptun(kcp_file) then
return nil, translate("Not a Kcptun executable file")
end
end
o = s:option(Value, "kcp_port", translate("KcpTun Port"))
o.datatype = "port"
o.default = 4000
function o.validate(self, value, section)
local kcp_file = "/usr/bin/kcptun-client"
local enable = kcp_enable:formvalue(section) or kcp_enable.disabled
if enable == kcp_enable.enabled then
if not nixio.fs.access(kcp_file) then
return nil, translate("Haven't a Kcptun executable file")
elseif not isKcptun(kcp_file) then
return nil, translate("Not a Kcptun executable file")
end
end
return value
end
o:depends("type", "ssr")
o:depends("type", "ss")
return value
end
o:depends("type", "ssr")
o:depends("type", "ss")
o = s:option(Value, "kcp_password", translate("KcpTun Password"))
o.password = true
o:depends("type", "ssr")
o:depends("type", "ss")
o = s:option(Value, "kcp_password", translate("KcpTun Password"))
o.password = true
o:depends("type", "ssr")
o:depends("type", "ss")
o = s:option(Value, "kcp_param", translate("KcpTun Param"))
o.default = "--nocomp"
o:depends("type", "ssr")
o:depends("type", "ss")
o = s:option(Value, "kcp_param", translate("KcpTun Param"))
o.default = "--nocomp"
o:depends("type", "ssr")
o:depends("type", "ss")
end
return m

View File

@ -1,27 +1,24 @@
-- Copyright (C) 2017 yushi studio <ywb94@qq.com> github.com/ywb94
-- Copyright (C) 2018 lean <coolsnowwolf@gmail.com> github.com/coolsnowwolf
-- Licensed to the public under the GNU General Public License v3.
local m, s, sec, o, kcp_enable
local shadowsocksr = "shadowsocksr"
local uci = luci.model.uci.cursor()
m = Map(shadowsocksr, translate("ShadowSocksR Plus+ Settings"),
translate("<h3>Support SS/SSR/V2RAY/TROJAN/NAIVEPROXY/SOCKS5/TUN etc.</h3>"))
m = Map("shadowsocksr", translate("ShadowSocksR Plus+ Settings"), translate("<h3>Support SS/SSR/V2RAY/TROJAN/NAIVEPROXY/SOCKS5/TUN etc.</h3>"))
m:section(SimpleSection).template = "shadowsocksr/status"
local server_table = {}
uci:foreach(shadowsocksr, "servers", function(s)
uci:foreach("shadowsocksr", "servers", function(s)
if s.alias then
server_table[s[".name"]] = "[%s]:%s" %{string.upper(s.type), s.alias}
server_table[s[".name"]] = "[%s]:%s" % {string.upper(s.type), s.alias}
elseif s.server and s.server_port then
server_table[s[".name"]] = "[%s]:%s:%s" %{string.upper(s.type), s.server, s.server_port}
server_table[s[".name"]] = "[%s]:%s:%s" % {string.upper(s.type), s.server, s.server_port}
end
end)
local key_table = {}
for key,_ in pairs(server_table) do
table.insert(key_table,key)
for key, _ in pairs(server_table) do
table.insert(key_table, key)
end
table.sort(key_table)
@ -32,26 +29,32 @@ s.anonymous = true
o = s:option(ListValue, "global_server", translate("Main Server"))
o:value("nil", translate("Disable"))
for _,key in pairs(key_table) do o:value(key,server_table[key]) end
for _, key in pairs(key_table) do
o:value(key, server_table[key])
end
o.default = "nil"
o.rmempty = false
o = s:option(ListValue, "udp_relay_server", translate("Game Mode UDP Server"))
o:value("", translate("Disable"))
o:value("same", translate("Same as Global Server"))
for _,key in pairs(key_table) do o:value(key,server_table[key]) end
for _, key in pairs(key_table) do
o:value(key, server_table[key])
end
o = s:option(ListValue, "netflix_server", translate("Netflix Node"))
o:value("nil", translate("Disable"))
o:value("same", translate("Same as Global Server"))
for _,key in pairs(key_table) do o:value(key,server_table[key]) end
for _, key in pairs(key_table) do
o:value(key, server_table[key])
end
o.default = "nil"
o.rmempty = false
o = s:option(Flag, "netflix_proxy", translate("External Proxy Mode"))
o.rmempty = false
o.description = translate("Forward Netflix Proxy through Main Proxy")
o.default="0"
o.default = "0"
o = s:option(ListValue, "threads", translate("Multi Threads Option"))
o:value("0", translate("Auto Threads"))

View File

@ -36,7 +36,7 @@ o.rmempty = false
o = s:taboption("lan_ac", DynamicList, "lan_ac_ips", translate("LAN Host List"))
o.datatype = "ipaddr"
luci.ip.neighbors({ family = 4 }, function(entry)
luci.ip.neighbors({family = 4}, function(entry)
if entry.reachable then
o:value(entry.dest:string())
end
@ -46,7 +46,7 @@ o:depends("lan_ac_mode", "b")
o = s:taboption("lan_ac", DynamicList, "lan_bp_ips", translate("LAN Bypassed Host List"))
o.datatype = "ipaddr"
luci.ip.neighbors({ family = 4 }, function(entry)
luci.ip.neighbors({family = 4}, function(entry)
if entry.reachable then
o:value(entry.dest:string())
end
@ -54,7 +54,7 @@ end)
o = s:taboption("lan_ac", DynamicList, "lan_fp_ips", translate("LAN Force Proxy Host List"))
o.datatype = "ipaddr"
luci.ip.neighbors({ family = 4 }, function(entry)
luci.ip.neighbors({family = 4}, function(entry)
if entry.reachable then
o:value(entry.dest:string())
end
@ -62,7 +62,7 @@ end)
o = s:taboption("lan_ac", DynamicList, "lan_gm_ips", translate("Game Mode Host List"))
o.datatype = "ipaddr"
luci.ip.neighbors({ family = 4 }, function(entry)
luci.ip.neighbors({family = 4}, function(entry)
if entry.reachable then
o:value(entry.dest:string())
end
@ -77,7 +77,7 @@ end)
-- o.rmempty = false
s:tab("esc", translate("Bypass Domain List"))
local escconf = "/etc/ssr/white.list"
local escconf = "/etc/ssrplus/white.list"
o = s:taboption("esc", TextValue, "escconf")
o.rows = 13
o.wrap = "off"
@ -93,7 +93,7 @@ o.remove = function(self, section, value)
end
s:tab("block", translate("Black Domain List"))
local blockconf = "/etc/ssr/black.list"
local blockconf = "/etc/ssrplus/black.list"
o = s:taboption("block", TextValue, "blockconf")
o.rows = 13
o.wrap = "off"
@ -109,7 +109,7 @@ o.remove = function(self, section, value)
end
s:tab("denydomain", translate("Deny Domain List"))
local denydomainconf = "/etc/ssr/deny.list"
local denydomainconf = "/etc/ssrplus/deny.list"
o = s:taboption("denydomain", TextValue, "denydomainconf")
o.rows = 13
o.wrap = "off"
@ -125,7 +125,7 @@ o.remove = function(self, section, value)
end
s:tab("netflix", translate("Netflix Domain List"))
local netflixconf = "/etc/ssr/netflix.list"
local netflixconf = "/etc/ssrplus/netflix.list"
o = s:taboption("netflix", TextValue, "netflixconf")
o.rows = 13
o.wrap = "off"

View File

@ -7,8 +7,8 @@ t = f:field(TextValue, "conf")
t.rmempty = true
t.rows = 20
function t.cfgvalue()
if nixio.fs.access("/tmp/ssrplus.log") then
local logs = luci.util.execi("cat /tmp/ssrplus.log")
if nixio.fs.access("/var/log/ssrplus.log") then
local logs = luci.util.execi("cat /var/log/ssrplus.log")
local s = ""
for line in logs do
s = line .. "\n" .. s
@ -16,5 +16,5 @@ function t.cfgvalue()
return s
end
end
t.readonly="readonly"
t.readonly = "readonly"
return f

View File

@ -5,48 +5,41 @@ require "luci.dispatcher"
require "nixio.fs"
local m, s, o
local shadowsocksr = "shadowsocksr"
local sid = arg[1]
local encrypt_methods = {
"rc4-md5",
"rc4-md5-6",
"rc4",
"table",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"bf-cfb",
"camellia-128-cfb",
"camellia-192-cfb",
"camellia-256-cfb",
"cast5-cfb",
"des-cfb",
"idea-cfb",
"rc2-cfb",
"seed-cfb",
"salsa20",
"chacha20",
"chacha20-ietf",
"rc4-md5",
"rc4-md5-6",
"rc4",
"table",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"bf-cfb",
"camellia-128-cfb",
"camellia-192-cfb",
"camellia-256-cfb",
"cast5-cfb",
"des-cfb",
"idea-cfb",
"rc2-cfb",
"seed-cfb",
"salsa20",
"chacha20",
"chacha20-ietf"
}
local protocol = {
"origin",
}
local protocol = {"origin"}
obfs = {
"plain",
"http_simple",
"http_post",
}
obfs = {"plain", "http_simple", "http_post"}
m = Map(shadowsocksr, translate("Edit ShadowSocksR Server"))
m = Map("shadowsocksr", translate("Edit ShadowSocksR Server"))
m.redirect = luci.dispatcher.build_url("admin/services/shadowsocksr/server")
if m.uci:get(shadowsocksr, sid) ~= "server_config" then
if m.uci:get("shadowsocksr", sid) ~= "server_config" then
luci.http.redirect(m.redirect)
return
end
@ -70,7 +63,7 @@ o.default = "socks5"
o = s:option(Value, "server_port", translate("Server Port"))
o.datatype = "port"
math.randomseed(tostring(os.time()):reverse():sub(1, 7))
o.default = math.random(10240,20480)
o.default = math.random(10240, 20480)
o.rmempty = false
o.description = translate("warning! Please do not reuse the port!")
@ -89,17 +82,23 @@ o.password = true
o.rmempty = false
o = s:option(ListValue, "encrypt_method", translate("Encrypt Method"))
for _, v in ipairs(encrypt_methods) do o:value(v) end
for _, v in ipairs(encrypt_methods) do
o:value(v)
end
o.rmempty = false
o:depends("type", "ssr")
o = s:option(ListValue, "protocol", translate("Protocol"))
for _, v in ipairs(protocol) do o:value(v) end
for _, v in ipairs(protocol) do
o:value(v)
end
o.rmempty = false
o:depends("type", "ssr")
o = s:option(ListValue, "obfs", translate("Obfs"))
for _, v in ipairs(obfs) do o:value(v) end
for _, v in ipairs(obfs) do
o:value(v)
end
o.rmempty = false
o:depends("type", "ssr")

View File

@ -3,51 +3,50 @@
require "luci.http"
require "luci.dispatcher"
local m, sec, o
local shadowsocksr = "shadowsocksr"
local encrypt_methods = {
"table",
"rc4",
"rc4-md5",
"rc4-md5-6",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"bf-cfb",
"camellia-128-cfb",
"camellia-192-cfb",
"camellia-256-cfb",
"cast5-cfb",
"des-cfb",
"idea-cfb",
"rc2-cfb",
"seed-cfb",
"salsa20",
"chacha20",
"chacha20-ietf",
"table",
"rc4",
"rc4-md5",
"rc4-md5-6",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"bf-cfb",
"camellia-128-cfb",
"camellia-192-cfb",
"camellia-256-cfb",
"cast5-cfb",
"des-cfb",
"idea-cfb",
"rc2-cfb",
"seed-cfb",
"salsa20",
"chacha20",
"chacha20-ietf"
}
local protocol = {
"origin",
"verify_deflate",
"auth_sha1_v4",
"auth_aes128_sha1",
"auth_aes128_md5",
"auth_chain_a",
"origin",
"verify_deflate",
"auth_sha1_v4",
"auth_aes128_sha1",
"auth_aes128_md5",
"auth_chain_a"
}
obfs = {
"plain",
"http_simple",
"http_post",
"random_head",
"tls1.2_ticket_auth",
"tls1.2_ticket_fastauth",
"plain",
"http_simple",
"http_post",
"random_head",
"tls1.2_ticket_auth",
"tls1.2_ticket_fastauth"
}
m = Map(shadowsocksr)
m = Map("shadowsocksr")
-- [[ Global Setting ]]--
sec = m:section(TypedSection, "server_global", translate("Global Setting"))
sec.anonymous = true

View File

@ -3,17 +3,15 @@ require "luci.http"
require "luci.dispatcher"
require "luci.model.uci"
local m, s, o
local shadowsocksr = "shadowsocksr"
local uci = luci.model.uci.cursor()
local server_count = 0
uci:foreach("shadowsocksr", "servers", function(s)
server_count = server_count + 1
end)
m = Map(shadowsocksr, translate("Servers subscription and manage"))
m = Map("shadowsocksr", translate("Servers subscription and manage"))
-- Server Subscribe
s = m:section(TypedSection, "server_subscribe")
s.anonymous = true
@ -21,12 +19,11 @@ o = s:option(Flag, "auto_update", translate("Auto Update"))
o.rmempty = false
o.description = translate("Auto Update Server subscription, GFW list and CHN route")
o = s:option(ListValue, "auto_update_time", translate("Update time (every day)"))
for t = 0,23 do
o:value(t, t..":00")
for t = 0, 23 do
o:value(t, t .. ":00")
end
o.default=2
o.default = 2
o.rmempty = false
o = s:option(DynamicList, "subscribe_url", translate("Subscribe URL"))
@ -36,28 +33,28 @@ o = s:option(Value, "filter_words", translate("Subscribe Filter Words"))
o.rmempty = true
o.description = translate("Filter Words splited by /")
o = s:option(Button,"update_Sub",translate("Update Subscribe List"))
o = s:option(Button, "update_Sub", translate("Update Subscribe List"))
o.inputstyle = "reload"
o.description = translate("Update subscribe url list first")
o.write = function()
uci:commit("shadowsocksr")
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "servers"))
end
o = s:option(Flag, "switch", translate("Subscribe Default Auto-Switch"))
o.rmempty = false
o.description = translate("Subscribe new add server default Auto-Switch on")
o.default="1"
o.default = "1"
o = s:option(Flag, "proxy", translate("Through proxy update"))
o.rmempty = false
o.description = translate("Through proxy update list, Not Recommended ")
o = s:option(Button,"subscribe", translate("Update All Subscribe Severs"))
o = s:option(Button, "subscribe", translate("Update All Subscribe Severs"))
o.rawhtml = true
o.template = "shadowsocksr/subscribe"
o = s:option(Button,"delete",translate("Delete All Subscribe Severs"))
o = s:option(Button, "delete", translate("Delete All Subscribe Severs"))
o.inputstyle = "reset"
o.description = string.format(translate("Server Count") .. ": %d", server_count)
o.write = function()
@ -69,9 +66,7 @@ o.write = function()
end
end)
uci:save("shadowsocksr")
uci:commit("shadowsocksr")
luci.sys.exec("/etc/init.d/shadowsocksr restart &")
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "servers"))
m.uci:commit("shadowsocksr")
return
end
@ -92,7 +87,7 @@ end
o = s:option(DummyValue, "type", translate("Type"))
function o.cfgvalue(...)
return (Value.cfgvalue(...) == "vless") and "VLESS" or Value.cfgvalue(...)
return Value.cfgvalue(...)
end
o = s:option(DummyValue, "alias", translate("Alias"))
@ -106,21 +101,19 @@ function o.cfgvalue(...)
end
o = s:option(DummyValue, "server_port", translate("Socket Connected"))
o.template="shadowsocksr/socket"
o.width="10%"
o.template = "shadowsocksr/socket"
o.width = "10%"
o = s:option(DummyValue, "server", translate("Ping Latency"))
o.template="shadowsocksr/ping"
o.width="10%"
o.template = "shadowsocksr/ping"
o.width = "10%"
node = s:option(Button,"apply_node",translate("Apply"))
node = s:option(Button, "apply_node", translate("Apply"))
node.inputstyle = "apply"
node.write = function(self, section)
uci:set("shadowsocksr", '@global[0]', 'global_server', section)
uci:save("shadowsocksr")
uci:commit("shadowsocksr")
luci.sys.exec("/etc/init.d/shadowsocksr restart &")
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "shadowsocksr", "client"))
m.uci:commit("shadowsocksr")
end
o = s:option(Flag, "switch_enable", translate("Auto Switch"))

View File

@ -4,177 +4,184 @@ require "nixio.fs"
require "luci.sys"
require "luci.model.uci"
local m, s, o
local redir_run=0
local reudp_run=0
local sock5_run=0
local server_run=0
local kcptun_run=0
local tunnel_run=0
local gfw_count=0
local ad_count=0
local ip_count=0
local nfip_count=0
local redir_run = 0
local reudp_run = 0
local sock5_run = 0
local server_run = 0
local kcptun_run = 0
local tunnel_run = 0
local gfw_count = 0
local ad_count = 0
local ip_count = 0
local nfip_count = 0
local Process_list = luci.sys.exec("busybox ps -w")
local uci = luci.model.uci.cursor()
local shadowsocksr = "shadowsocksr"
-- html constants
font_blue = [[<font color="green">]]
font_off = [[</font>]]
bold_on = [[<strong>]]
bold_off = [[</strong>]]
local kcptun_version=translate("Unknown")
local kcp_file="/usr/bin/kcptun-client"
local kcptun_version = translate("Unknown")
local kcp_file = "/usr/bin/kcptun-client"
if not nixio.fs.access(kcp_file) then
kcptun_version=translate("Not exist")
kcptun_version = translate("Not exist")
else
if not nixio.fs.access(kcp_file, "rwx", "rx", "rx") then
nixio.fs.chmod(kcp_file, 755)
end
kcptun_version=luci.sys.exec(kcp_file .. " -v | awk '{printf $3}'")
kcptun_version = luci.sys.exec(kcp_file .. " -v | awk '{printf $3}'")
if not kcptun_version or kcptun_version == "" then
kcptun_version = translate("Unknown")
end
end
if nixio.fs.access("/etc/ssr/gfw_list.conf") then
gfw_count = tonumber(luci.sys.exec("cat /etc/ssr/gfw_list.conf | wc -l"))/2
if nixio.fs.access("/etc/ssrplus/gfw_list.conf") then
gfw_count = tonumber(luci.sys.exec("cat /etc/ssrplus/gfw_list.conf | wc -l")) / 2
end
if nixio.fs.access("/etc/ssr/ad.conf") then
ad_count = tonumber(luci.sys.exec("cat /etc/ssr/ad.conf | wc -l"))
if nixio.fs.access("/etc/ssrplus/ad.conf") then
ad_count = tonumber(luci.sys.exec("cat /etc/ssrplus/ad.conf | wc -l"))
end
if nixio.fs.access("/etc/ssr/china_ssr.txt") then
ip_count = tonumber(luci.sys.exec("cat /etc/ssr/china_ssr.txt | wc -l"))
if nixio.fs.access("/etc/ssrplus/china_ssr.txt") then
ip_count = tonumber(luci.sys.exec("cat /etc/ssrplus/china_ssr.txt | wc -l"))
end
if nixio.fs.access("/etc/ssr/netflixip.list") then
nfip_count = tonumber(luci.sys.exec("cat /etc/ssr/netflixip.list | wc -l"))
if nixio.fs.access("/etc/ssrplus/netflixip.list") then
nfip_count = tonumber(luci.sys.exec("cat /etc/ssrplus/netflixip.list | wc -l"))
end
local icount=luci.sys.exec("busybox ps -w | grep ssr-reudp |grep -v grep| wc -l")
if tonumber(icount)>0 then
reudp_run=1
else
icount=luci.sys.exec("busybox ps -w | grep ssr-retcp |grep \"\\-u\"|grep -v grep| wc -l")
if tonumber(icount)>0 then
reudp_run=1
end
if Process_list:find("udp.only.ssr.reudp") then
reudp_run = 1
end
if luci.sys.call("busybox ps -w | grep ssr-retcp | grep -v grep >/dev/null") == 0 then
redir_run=1
if Process_list:find("tcp.only.ssr.retcp") then
redir_run = 1
end
if luci.sys.call("busybox ps -w | grep ssr-local | grep -v ssr-socksdns |grep -v grep >/dev/null") == 0 then
sock5_run=1
if Process_list:find("tcp.udp.ssr.local") then
sock5_run = 1
end
if luci.sys.call("pidof kcptun-client >/dev/null") == 0 then
kcptun_run=1
if Process_list:find("tcp.udp.ssr.retcp") then
redir_run = 1
reudp_run = 1
end
if luci.sys.call("busybox ps -w | grep ssr-server | grep -v grep >/dev/null") == 0 then
server_run=1
if Process_list:find("local.ssr.retcp") then
redir_run = 1
sock5_run = 1
end
-- if luci.sys.call("busybox ps -w | grep ssr-tunnel |grep -v grep >/dev/null") == 0 then
-- tunnel_run=1
-- end
if luci.sys.call("pidof pdnsd >/dev/null") == 0 or (luci.sys.call("busybox ps -w | grep ssr-dns |grep -v grep >/dev/null") == 0 and luci.sys.call("pidof dns2socks >/dev/null") == 0)then
pdnsd_run=1
if Process_list:find("local.udp.ssr.retcp") then
reudp_run = 1
redir_run = 1
sock5_run = 1
end
if Process_list:find("kcptun.client") then
kcptun_run = 1
end
if Process_list:find("ssr.server") then
server_run = 1
end
if Process_list:find("ssrplus/bin/pdnsd") or (Process_list:find("ssrplus.dns") and Process_list:find("dns2socks 127.0.0.1:.*127.0.0.1:5335")) then
pdnsd_run = 1
end
m = SimpleForm("Version")
m.reset = false
m.submit = false
s=m:field(DummyValue,"redir_run",translate("Global Client"))
s = m:field(DummyValue, "redir_run", translate("Global Client"))
s.rawhtml = true
if redir_run == 1 then
s.value =font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
s.value = font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
else
s.value = translate("Not Running")
end
s=m:field(DummyValue,"reudp_run",translate("Game Mode UDP Relay"))
s = m:field(DummyValue, "reudp_run", translate("Game Mode UDP Relay"))
s.rawhtml = true
if reudp_run == 1 then
s.value =font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
s.value = font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
else
s.value = translate("Not Running")
end
if uci:get_first(shadowsocksr, 'global', 'pdnsd_enable', '0') ~= '0' then
s=m:field(DummyValue,"pdnsd_run",translate("DNS Anti-pollution"))
if uci:get_first("shadowsocksr", 'global', 'pdnsd_enable', '0') ~= '0' then
s = m:field(DummyValue, "pdnsd_run", translate("DNS Anti-pollution"))
s.rawhtml = true
if pdnsd_run == 1 then
s.value =font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
s.value = font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
else
s.value = translate("Not Running")
end
end
s=m:field(DummyValue,"sock5_run",translate("Global SOCKS5 Proxy Server"))
s = m:field(DummyValue, "sock5_run", translate("Global SOCKS5 Proxy Server"))
s.rawhtml = true
if sock5_run == 1 then
s.value =font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
s.value = font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
else
s.value = translate("Not Running")
end
s=m:field(DummyValue,"server_run",translate("Local Servers"))
s = m:field(DummyValue, "server_run", translate("Local Servers"))
s.rawhtml = true
if server_run == 1 then
s.value =font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
s.value = font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
else
s.value = translate("Not Running")
end
if nixio.fs.access("/usr/bin/kcptun-client") then
s=m:field(DummyValue,"kcp_version",translate("KcpTun Version"))
s = m:field(DummyValue, "kcp_version", translate("KcpTun Version"))
s.rawhtml = true
s.value =kcptun_version
s=m:field(DummyValue,"kcptun_run",translate("KcpTun"))
s.value = kcptun_version
s = m:field(DummyValue, "kcptun_run", translate("KcpTun"))
s.rawhtml = true
if kcptun_run == 1 then
s.value =font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
s.value = font_blue .. bold_on .. translate("Running") .. bold_off .. font_off
else
s.value = translate("Not Running")
end
end
s=m:field(DummyValue,"google",translate("Google Connectivity"))
s = m:field(DummyValue, "google", translate("Google Connectivity"))
s.value = translate("No Check")
s.template = "shadowsocksr/check"
s=m:field(DummyValue,"baidu",translate("Baidu Connectivity"))
s = m:field(DummyValue, "baidu", translate("Baidu Connectivity"))
s.value = translate("No Check")
s.template = "shadowsocksr/check"
s=m:field(DummyValue,"gfw_data",translate("GFW List Data"))
s = m:field(DummyValue, "gfw_data", translate("GFW List Data"))
s.rawhtml = true
s.template = "shadowsocksr/refresh"
s.value = gfw_count .. " " .. translate("Records")
s=m:field(DummyValue,"ip_data",translate("China IP Data"))
s = m:field(DummyValue, "ip_data", translate("China IP Data"))
s.rawhtml = true
s.template = "shadowsocksr/refresh"
s.value = ip_count .. " " .. translate("Records")
s=m:field(DummyValue,"nfip_data",translate("Netflix IP Data"))
s = m:field(DummyValue, "nfip_data", translate("Netflix IP Data"))
s.rawhtml = true
s.template = "shadowsocksr/refresh"
s.value = nfip_count .. " " .. translate("Records")
if uci:get_first(shadowsocksr, 'global', 'adblock', '0') == '1' then
s=m:field(DummyValue,"ad_data",translate("Advertising Data"))
if uci:get_first("shadowsocksr", 'global', 'adblock', '0') == '1' then
s = m:field(DummyValue, "ad_data", translate("Advertising Data"))
s.rawhtml = true
s.template = "shadowsocksr/refresh"
s.value = ad_count .. " " .. translate("Records")
end
s=m:field(DummyValue,"check_port",translate("Check Server Port"))
s = m:field(DummyValue, "check_port", translate("Check Server Port"))
s.template = "shadowsocksr/checkport"
s.value =translate("No Check")
s.value = translate("No Check")
return m

View File

@ -26,5 +26,4 @@
//]]></script>
<input type="button" class="cbi-button cbi-button-apply" value="<%:Check Connect%>" onclick="return check_connect(this,'<%=self.option%>')" />
<span id="<%=self.option%>-status"><em><%=self.value%></em></span>
<%+cbi/valuefooter%>

View File

@ -1,7 +1,5 @@
<%+cbi/valueheader%>
<script type="text/javascript">//<![CDATA[
function check_port(btn)
{
btn.disabled = true;
@ -13,24 +11,15 @@
var s = document.getElementById('<%=self.option%>-status');
if (s)
{
s.innerHTML =rv.ret;
}
btn.disabled = false;
btn.value = '<%:Check Server%>';
}
);
return false;
}
//]]></script>
<input type="button" class="cbi-button cbi-button-apply" value="<%:Check Server%>" onclick="return check_port(this)" />
<span id="<%=self.option%>-status"><em><%=self.value%></em></span>
<%+cbi/valuefooter%>
<%+cbi/valuefooter%>

View File

@ -12,16 +12,18 @@
var s = document.getElementById(dataname+'-status');
if (s)
{
if (rv.ret=="0")
s.innerHTML ="<font color='green'>"+"<%:No new data!%> "+"</font>";
else if(rv.ret=="-1")
if(rv.ret=="-1")
{
s.innerHTML ="<font color='red'>"+"<%:Refresh Error!%> "+"</font>";
}
if (rv.ret=="0")
{
s.innerHTML ="<font color='green'>"+"<%:No new data!%> "+"</font>";
}
else
{
s.innerHTML ="<font color='green'>"+"<%:Refresh OK!%> "+"<%:Total Records:%>"+rv.ret+"</font>";
}
{
s.innerHTML ="<font color='green'>"+"<%:Refresh OK!%> "+"<%:Total Records:%>"+rv.ret+"</font>";
}
}
btn.disabled = false;
btn.value = '<%:Refresh Data %>';

View File

@ -0,0 +1,25 @@
<%+cbi/valueheader%>
<script type="text/javascript">//<![CDATA[
function subscribe(btn,dataname) {
var s = document.getElementById(dataname + '-status');
var reset = prompt('<%:Really reset all changes?%><%:Please fill in reset%>',"")
if (reset == null || reset == "") {
return false;
}
if (reset != "reset") {
s.innerHTML = "<font color='red'><%:The content entered is incorrect!%></font>";
return false;
}
btn.disabled = true;
btn.value = '<%:Perform reset%>';
murl=dataname;
XHR.get('<%=luci.dispatcher.build_url("admin", "services", "shadowsocksr","reset")%>', { set:murl }, function(x,rv) {
btn.value = '<%:Reset complete%>';
s.innerHTML = "<font color='green'><%:Reset complete%></font>";
});
return false;
}
//]]></script>
<input type="button" class="cbi-button cbi-button-reset" value="<%:Perform reset%> " onclick="return subscribe(this,'<%=self.option%>')" />
<span id="<%=self.option%>-status"></span>
<%+cbi/valuefooter%>

View File

@ -2,9 +2,6 @@
Copyright 2018-2019 Lienol <lawlienol@gmail.com>
Licensed to the public under the Apache License 2.0.
-%>
<%
local dsp = require "luci.dispatcher"
-%>
<script type="text/javascript">
//<![CDATA[
const doms = document.getElementsByClassName('pingtime');
@ -15,8 +12,7 @@ local dsp = require "luci.dispatcher"
const port = ports[index];
if (!dom) res()
port.innerHTML = '<font color="#0072c3">connect</font>';
XHR.get('<%=dsp.build_url("admin/services/shadowsocksr/ping")%>', {
XHR.get('<%=luci.dispatcher.build_url("admin/services/shadowsocksr/ping")%>', {
index,
domain: dom.getAttribute("hint"),
port: port.getAttribute("hint")
@ -48,7 +44,6 @@ local dsp = require "luci.dispatcher"
for (let i = 0; i < 20; i++) {
thread()
}
function cbi_row_drop(fromId, toId, store, isToBottom) {
var fromNode = document.getElementById(fromId);
var toNode = document.getElementById(toId);
@ -65,7 +60,6 @@ local dsp = require "luci.dispatcher"
} else {
fromNode.parentNode.insertBefore(fromNode, toNode);
}
for (var idx = 2; idx < table.rows.length; idx++) {
table.rows[idx].className = table.rows[idx].className.replace(
/cbi-rowstyle-[12]/,
@ -75,53 +69,43 @@ local dsp = require "luci.dispatcher"
if (table.rows[idx].id && table.rows[idx].id.match(/-([^\-]+)$/))
ids.push(RegExp.$1);
}
var input = document.getElementById(store);
if (input) input.value = ids.join(" ");
return false;
}
// set tr draggable
function enableDragForTable(table_selecter, store) {
var trs = document.querySelectorAll(table_selecter + " tr");
if (!trs || trs.length.length < 3) {
return;
}
function ondragstart(ev) {
ev.dataTransfer.setData("Text", ev.target.id);
}
function ondrop(ev) {
var from = ev.dataTransfer.getData("Text");
cbi_row_drop(from, this.id, store);
}
function ondragover(ev) {
ev.preventDefault();
ev.dataTransfer.dropEffect = "move";
}
function moveToTop(id) {
var top = document.querySelectorAll(table_selecter + " tr")[2];
cbi_row_drop(id, top.id, store);
}
function moveToBottom(id) {
console.log('moveToBottom:', id);
var trList = document.querySelectorAll(table_selecter + " tr");
var bottom = trList[trList.length - 1];
cbi_row_drop(id, bottom.id, store, true);
}
for (let index = 2; index < trs.length; index++) {
const el = trs[index];
el.setAttribute("draggable", true);
el.ondragstart = ondragstart;
el.ondrop = ondrop;
el.ondragover = ondragover;
// reset the behaviors of the btns
var upBtns = el.querySelectorAll(".cbi-button.cbi-button-up");
if (upBtns && upBtns.length > 0) {
@ -131,7 +115,6 @@ local dsp = require "luci.dispatcher"
};
});
}
var downBtns = el.querySelectorAll(".cbi-button.cbi-button-down");
if (downBtns && downBtns.length > 0) {
downBtns.forEach(function (_el) {
@ -142,7 +125,6 @@ local dsp = require "luci.dispatcher"
}
}
}
// enable
enableDragForTable(
"#cbi-shadowsocksr-servers table",

View File

@ -80,7 +80,6 @@
}
return false;
}
function import_ssr_url(btn, urlname, sid) {
var s = document.getElementById(urlname + '-status');
if (!s)
@ -98,7 +97,6 @@
s.innerHTML = "<font color='red'>无效格式</font>";
return false;
}
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
if (ssu[0] == "ssr") {
@ -137,7 +135,6 @@
s.innerHTML = "<font color='green'>导入ShadowsocksR配置信息成功</font>";
return false;
} else if (ssu[0] == "ss") {
var url0, param = "";
var sipIndex = ssu[1].indexOf("@");
var ploc = ssu[1].indexOf("#");
@ -147,7 +144,6 @@
} else {
url0 = ssu[1];
}
if (sipIndex != -1) {
// SIP002
var userInfo = b64decsafe(url0.substr(0, sipIndex));
@ -163,7 +159,6 @@
plugin = pluginNameInfo.substr(pluginNameInfo.indexOf("=") + 1)
pluginOpts = pluginInfo.substr(pluginIndex + 1);
}
var userInfoSplitIndex = userInfo.indexOf(":");
if (userInfoSplitIndex != -1) {
method = userInfo.substr(0, userInfoSplitIndex);
@ -177,7 +172,6 @@
document.getElementsByName('cbid.shadowsocksr.' + sid + '.encrypt_method_ss')[0].value = method || "";
document.getElementsByName('cbid.shadowsocksr.' + sid + '.plugin')[0].value = plugin || "";
document.getElementsByName('cbid.shadowsocksr.' + sid + '.plugin_opts')[0].value = pluginOpts || "";
if (param != undefined) {
document.getElementsByName('cbid.shadowsocksr.' + sid + '.alias')[0].value = decodeURI(param);
}
@ -212,7 +206,6 @@
url0 = ssu[1]
}
var sstr = url0;
document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].value = "trojan";
document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].dispatchEvent(event);
var team = sstr.split('@');
@ -229,7 +222,6 @@
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
}
}
document.getElementsByName('cbid.shadowsocksr.' + sid + '.server')[0].value = serverPart[0];
document.getElementsByName('cbid.shadowsocksr.' + sid + '.server_port')[0].value = port;
document.getElementsByName('cbid.shadowsocksr.' + sid + '.password')[0].value = password;
@ -246,7 +238,7 @@
} else if (ssu[0] == "vmess") {
var sstr = b64DecodeUnicode(ssu[1]);
var ploc = sstr.indexOf("/?");
document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].value = "v2ray";
document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].value = "vmess";
document.getElementsByName('cbid.shadowsocksr.' + sid + '.type')[0].dispatchEvent(event);
var url0, param = "";
if (ploc > 0) {

View File

@ -690,3 +690,57 @@ msgstr "混淆密码(可选)"
msgid "Select the interface that needs to transmit data. If unchecked, all interfaces will pass data by default!"
msgstr "选择需要传递数据的接口。如果未选择,则默认情况下所有接口都将传递数据!"
msgid "Please fill in the Host, for example: www.baidu.com"
msgstr "请填写Host例如www.baidu.com"
msgid "Camouflage Type"
msgstr "伪装类型"
msgid "VideoCall (SRTP)"
msgstr "视频通话 (SRTP)"
msgid "BitTorrent (uTP)"
msgstr "BT下载 (uTP)"
msgid "WechatVideo"
msgstr "微信视频通话"
msgid "DTLS 1.2"
msgstr "DTLS 1.2 数据包"
msgid "WireGuard"
msgstr "WireGuard 数据包"
msgid "MTU"
msgstr "最大传输单元"
msgid "TTI"
msgstr "传输时间间隔"
msgid "Uplink Capacity"
msgstr "上行链路容量"
msgid "Downlink Capacity"
msgstr "下行链路容量"
msgid "Read Buffer Size"
msgstr "读取缓冲区大小"
msgid "Write Buffer Size"
msgstr "写入缓冲区大小"
msgid "Congestion"
msgstr "拥塞控制"
msgid "Network interface to use"
msgstr "使用的网络接口"
msgid "Please fill in reset"
msgstr "请填写 reset"
msgid "The content entered is incorrect!"
msgstr "输入的内容不正确!"
msgid "Reset complete"
msgstr "重置完成"

View File

@ -1,42 +0,0 @@
config global
option global_server 'nil'
option netflix_server 'nil'
option netflix_proxy '0'
option threads '0'
option run_mode 'router'
option dports '2'
option pdnsd_enable '1'
option tunnel_forward '8.8.4.4:53'
option monitor_enable '1'
option enable_switch '1'
option switch_time '667'
option switch_timeout '5'
option switch_try_count '3'
option gfwlist_url 'https://cdn.jsdelivr.net/gh/v2fly/domain-list-community@release/gfwlist.txt'
option chnroute_url 'https://ispip.clang.cn/all_cn.txt'
option nfip_url 'https://cdn.jsdelivr.net/gh/QiuSimons/Netflix_IP/getflix.txt'
option adblock_url 'https://anti-ad.net/anti-ad-for-dnsmasq.conf'
config access_control
option lan_ac_mode '0'
option router_proxy '1'
list wan_fw_ips '149.154.160.0/20'
list wan_fw_ips '67.198.55.0/24'
list wan_fw_ips '91.108.4.0/22'
list wan_fw_ips '91.108.56.0/22'
list wan_fw_ips '109.239.140.0/24'
list Interface 'lan'
config server_subscribe
option proxy '0'
option auto_update_time '2'
option auto_update '1'
option filter_words '过期时间/剩余流量/QQ群/官网/防失联地址/回国'
config socks5_proxy
option socks '0'
option local_port '1080'
config server_global
option enable_server '0'

File diff suppressed because it is too large Load Diff

View File

@ -4104,6 +4104,13 @@
103.158.224.0/23
103.159.80.0/23
103.159.122.0/23
103.159.124.0/23
103.159.134.0/23
103.159.142.0/23
103.160.32.0/23
103.160.34.0/23
103.160.112.0/23
103.160.114.0/23
103.192.0.0/22
103.192.4.0/22
103.192.8.0/22
@ -4335,7 +4342,6 @@
103.203.24.0/22
103.203.28.0/22
103.203.32.0/22
103.203.52.0/22
103.203.56.0/22
103.203.96.0/22
103.203.100.0/22

View File

@ -484,6 +484,8 @@ server=/airmax360.com/127.0.0.1#5335
ipset=/airmax360.com/gfwlist
server=/airmay.com/127.0.0.1#5335
ipset=/airmay.com/gfwlist
server=/airport-gov-cn.com/127.0.0.1#5335
ipset=/airport-gov-cn.com/gfwlist
server=/airport.brussels/127.0.0.1#5335
ipset=/airport.brussels/gfwlist
server=/airport.com/127.0.0.1#5335
@ -2032,6 +2034,8 @@ server=/bandwagonhost.com/127.0.0.1#5335
ipset=/bandwagonhost.com/gfwlist
server=/bangbros.com/127.0.0.1#5335
ipset=/bangbros.com/gfwlist
server=/banned.video/127.0.0.1#5335
ipset=/banned.video/gfwlist
server=/barefootnetworks.com/127.0.0.1#5335
ipset=/barefootnetworks.com/gfwlist
server=/barrons.com/127.0.0.1#5335
@ -2074,6 +2078,8 @@ server=/baxsound.com/127.0.0.1#5335
ipset=/baxsound.com/gfwlist
server=/bayareabmw.com/127.0.0.1#5335
ipset=/bayareabmw.com/gfwlist
server=/bayvoice.net/127.0.0.1#5335
ipset=/bayvoice.net/gfwlist
server=/bazel.build/127.0.0.1#5335
ipset=/bazel.build/gfwlist
server=/bbc.co.uk/127.0.0.1#5335
@ -4696,8 +4702,6 @@ server=/chimeforchange.org/127.0.0.1#5335
ipset=/chimeforchange.org/gfwlist
server=/china-facebook.com/127.0.0.1#5335
ipset=/china-facebook.com/gfwlist
server=/china-internet-exchange.com/127.0.0.1#5335
ipset=/china-internet-exchange.com/gfwlist
server=/chinaclothesstore.com/127.0.0.1#5335
ipset=/chinaclothesstore.com/gfwlist
server=/chinadecoding.com/127.0.0.1#5335
@ -4912,12 +4916,16 @@ server=/cmhalq.com/127.0.0.1#5335
ipset=/cmhalq.com/gfwlist
server=/cmpaas.com/127.0.0.1#5335
ipset=/cmpaas.com/gfwlist
server=/cmu.edu/127.0.0.1#5335
ipset=/cmu.edu/gfwlist
server=/cnbc.com/127.0.0.1#5335
ipset=/cnbc.com/gfwlist
server=/cnbcfm.com/127.0.0.1#5335
ipset=/cnbcfm.com/gfwlist
server=/cncrivals.com/127.0.0.1#5335
ipset=/cncrivals.com/gfwlist
server=/cnix-gov-cn.com/127.0.0.1#5335
ipset=/cnix-gov-cn.com/gfwlist
server=/cnn.com/127.0.0.1#5335
ipset=/cnn.com/gfwlist
server=/cnn.io/127.0.0.1#5335
@ -5208,8 +5216,6 @@ server=/cutt.ly/127.0.0.1#5335
ipset=/cutt.ly/gfwlist
server=/cvnad.com/127.0.0.1#5335
ipset=/cvnad.com/gfwlist
server=/cxkcloud.com/127.0.0.1#5335
ipset=/cxkcloud.com/gfwlist
server=/cyber-bay.info/127.0.0.1#5335
ipset=/cyber-bay.info/gfwlist
server=/cyber-bay.org/127.0.0.1#5335
@ -5510,6 +5516,8 @@ server=/digital-rb.com/127.0.0.1#5335
ipset=/digital-rb.com/gfwlist
server=/digitalassetlinks.org/127.0.0.1#5335
ipset=/digitalassetlinks.org/gfwlist
server=/digitalcertvalidation.com/127.0.0.1#5335
ipset=/digitalcertvalidation.com/gfwlist
server=/digitalhub.com/127.0.0.1#5335
ipset=/digitalhub.com/gfwlist
server=/digitalid.ch/127.0.0.1#5335
@ -5822,6 +5830,8 @@ server=/dleris.best/127.0.0.1#5335
ipset=/dleris.best/gfwlist
server=/dlfacebook.com/127.0.0.1#5335
ipset=/dlfacebook.com/gfwlist
server=/dlgarenanow-a.akamaihd.net/127.0.0.1#5335
ipset=/dlgarenanow-a.akamaihd.net/gfwlist
server=/dlmobilegarena-a.akamaihd.net/127.0.0.1#5335
ipset=/dlmobilegarena-a.akamaihd.net/gfwlist
server=/dmgmediaprivacy.co.uk/127.0.0.1#5335
@ -6672,6 +6682,8 @@ server=/edgefonts.net/127.0.0.1#5335
ipset=/edgefonts.net/gfwlist
server=/edisebay.com/127.0.0.1#5335
ipset=/edisebay.com/gfwlist
server=/edninfo.com/127.0.0.1#5335
ipset=/edninfo.com/gfwlist
server=/edu-research.org/127.0.0.1#5335
ipset=/edu-research.org/gfwlist
server=/edx-cdn.org/127.0.0.1#5335
@ -6818,12 +6830,28 @@ server=/epinions.com/127.0.0.1#5335
ipset=/epinions.com/gfwlist
server=/episodic.com/127.0.0.1#5335
ipset=/episodic.com/gfwlist
server=/epoch.cloud/127.0.0.1#5335
ipset=/epoch.cloud/gfwlist
server=/epoch.org.il/127.0.0.1#5335
ipset=/epoch.org.il/gfwlist
server=/epochbuy.com/127.0.0.1#5335
ipset=/epochbuy.com/gfwlist
server=/epochcar.com/127.0.0.1#5335
ipset=/epochcar.com/gfwlist
server=/epochhk.com/127.0.0.1#5335
ipset=/epochhk.com/gfwlist
server=/epochmall.com/127.0.0.1#5335
ipset=/epochmall.com/gfwlist
server=/epochmediagroup.com/127.0.0.1#5335
ipset=/epochmediagroup.com/gfwlist
server=/epochshop.com/127.0.0.1#5335
ipset=/epochshop.com/gfwlist
server=/epochstories.com/127.0.0.1#5335
ipset=/epochstories.com/gfwlist
server=/epochtime.com/127.0.0.1#5335
ipset=/epochtime.com/gfwlist
server=/epochtimes-bg.com/127.0.0.1#5335
ipset=/epochtimes-bg.com/gfwlist
server=/epochtimes-romania.com/127.0.0.1#5335
ipset=/epochtimes-romania.com/gfwlist
server=/epochtimes.co.il/127.0.0.1#5335
@ -6854,6 +6882,8 @@ server=/epochtimes.ru/127.0.0.1#5335
ipset=/epochtimes.ru/gfwlist
server=/epochtimes.se/127.0.0.1#5335
ipset=/epochtimes.se/gfwlist
server=/epochtimeshk.org/127.0.0.1#5335
ipset=/epochtimeshk.org/gfwlist
server=/epochtimestr.com/127.0.0.1#5335
ipset=/epochtimestr.com/gfwlist
server=/epochweek.com/127.0.0.1#5335
@ -6892,6 +6922,8 @@ server=/ethereum.foundation/127.0.0.1#5335
ipset=/ethereum.foundation/gfwlist
server=/ethereum.org/127.0.0.1#5335
ipset=/ethereum.org/gfwlist
server=/etviet.com/127.0.0.1#5335
ipset=/etviet.com/gfwlist
server=/etvonline.hk/127.0.0.1#5335
ipset=/etvonline.hk/gfwlist
server=/eu-consumer-empowerment.com/127.0.0.1#5335
@ -8692,6 +8724,8 @@ server=/goldnikeclub.com/127.0.0.1#5335
ipset=/goldnikeclub.com/gfwlist
server=/golos-ameriki.ru/127.0.0.1#5335
ipset=/golos-ameriki.ru/gfwlist
server=/golosameriki.com/127.0.0.1#5335
ipset=/golosameriki.com/gfwlist
server=/gonglchuangl.net/127.0.0.1#5335
ipset=/gonglchuangl.net/gfwlist
server=/gonike.com/127.0.0.1#5335
@ -9212,6 +9246,8 @@ server=/handbagsoutletebay.com/127.0.0.1#5335
ipset=/handbagsoutletebay.com/gfwlist
server=/hanime.tv/127.0.0.1#5335
ipset=/hanime.tv/gfwlist
server=/hanime1.me/127.0.0.1#5335
ipset=/hanime1.me/gfwlist
server=/happymeal.co.nz/127.0.0.1#5335
ipset=/happymeal.co.nz/gfwlist
server=/happymeal.com.au/127.0.0.1#5335
@ -10156,6 +10192,10 @@ server=/industrialtoys.com/127.0.0.1#5335
ipset=/industrialtoys.com/gfwlist
server=/informs.org/127.0.0.1#5335
ipset=/informs.org/gfwlist
server=/infowars.com/127.0.0.1#5335
ipset=/infowars.com/gfwlist
server=/infowarsmedia.com/127.0.0.1#5335
ipset=/infowarsmedia.com/gfwlist
server=/infrapedia.com/127.0.0.1#5335
ipset=/infrapedia.com/gfwlist
server=/ingads.com/127.0.0.1#5335
@ -11034,6 +11074,8 @@ server=/jfrog.org/127.0.0.1#5335
ipset=/jfrog.org/gfwlist
server=/jgg18.xyz/127.0.0.1#5335
ipset=/jgg18.xyz/gfwlist
server=/jiayoulu.com/127.0.0.1#5335
ipset=/jiayoulu.com/gfwlist
server=/jibemobile.com/127.0.0.1#5335
ipset=/jibemobile.com/gfwlist
server=/jijiji.ca/127.0.0.1#5335
@ -11126,6 +11168,8 @@ server=/keytransparency.foo/127.0.0.1#5335
ipset=/keytransparency.foo/gfwlist
server=/keytransparency.org/127.0.0.1#5335
ipset=/keytransparency.org/gfwlist
server=/kfs.io/127.0.0.1#5335
ipset=/kfs.io/gfwlist
server=/khanacademy.org/127.0.0.1#5335
ipset=/khanacademy.org/gfwlist
server=/kickshatchannel.com/127.0.0.1#5335
@ -11210,6 +11254,12 @@ server=/kirbysuperstarultra.com/127.0.0.1#5335
ipset=/kirbysuperstarultra.com/gfwlist
server=/kissjav.com/127.0.0.1#5335
ipset=/kissjav.com/gfwlist
server=/kkbox.com/127.0.0.1#5335
ipset=/kkbox.com/gfwlist
server=/kktix.com/127.0.0.1#5335
ipset=/kktix.com/gfwlist
server=/kktv.me/127.0.0.1#5335
ipset=/kktv.me/gfwlist
server=/klik.me/127.0.0.1#5335
ipset=/klik.me/gfwlist
server=/knovel.com/127.0.0.1#5335
@ -13218,12 +13268,12 @@ server=/mzed.com/127.0.0.1#5335
ipset=/mzed.com/gfwlist
server=/mzstatic.com/127.0.0.1#5335
ipset=/mzstatic.com/gfwlist
server=/n3ro.fun/127.0.0.1#5335
ipset=/n3ro.fun/gfwlist
server=/n3ro.live/127.0.0.1#5335
ipset=/n3ro.live/gfwlist
server=/n3ro.lol/127.0.0.1#5335
ipset=/n3ro.lol/gfwlist
server=/n3ro.net/127.0.0.1#5335
ipset=/n3ro.net/gfwlist
server=/n3ro.wtf/127.0.0.1#5335
ipset=/n3ro.wtf/gfwlist
server=/na-att-idns.net/127.0.0.1#5335
ipset=/na-att-idns.net/gfwlist
server=/nab.com.au/127.0.0.1#5335
@ -13878,6 +13928,10 @@ server=/ntc.party/127.0.0.1#5335
ipset=/ntc.party/gfwlist
server=/ntd.com/127.0.0.1#5335
ipset=/ntd.com/gfwlist
server=/ntdca.com/127.0.0.1#5335
ipset=/ntdca.com/gfwlist
server=/ntdimg.com/127.0.0.1#5335
ipset=/ntdimg.com/gfwlist
server=/ntdtv.ca/127.0.0.1#5335
ipset=/ntdtv.ca/gfwlist
server=/ntdtv.co.kr/127.0.0.1#5335
@ -14346,6 +14400,8 @@ server=/oxfordscholarship.com/127.0.0.1#5335
ipset=/oxfordscholarship.com/gfwlist
server=/oxfordwesternmusic.com/127.0.0.1#5335
ipset=/oxfordwesternmusic.com/gfwlist
server=/ozvoice.org/127.0.0.1#5335
ipset=/ozvoice.org/gfwlist
server=/p-events-delivery.akamaized.net/127.0.0.1#5335
ipset=/p-events-delivery.akamaized.net/gfwlist
server=/p16-tiktokcdn-com.akamaized.net/127.0.0.1#5335
@ -14780,6 +14836,8 @@ server=/perl.org/127.0.0.1#5335
ipset=/perl.org/gfwlist
server=/persagg.com/127.0.0.1#5335
ipset=/persagg.com/gfwlist
server=/persianepochtimes.com/127.0.0.1#5335
ipset=/persianepochtimes.com/gfwlist
server=/personaltrainermath.com/127.0.0.1#5335
ipset=/personaltrainermath.com/gfwlist
server=/personeelsland.com/127.0.0.1#5335
@ -15212,6 +15270,8 @@ server=/protonmail.com/127.0.0.1#5335
ipset=/protonmail.com/gfwlist
server=/protonstatus.com/127.0.0.1#5335
ipset=/protonstatus.com/gfwlist
server=/proxyrarbg.org/127.0.0.1#5335
ipset=/proxyrarbg.org/gfwlist
server=/pscdn.co/127.0.0.1#5335
ipset=/pscdn.co/gfwlist
server=/pscp.tv/127.0.0.1#5335
@ -16014,6 +16074,8 @@ server=/securepaypal.info/127.0.0.1#5335
ipset=/securepaypal.info/gfwlist
server=/sensorynetworks.com/127.0.0.1#5335
ipset=/sensorynetworks.com/gfwlist
server=/seqingx.com/127.0.0.1#5335
ipset=/seqingx.com/gfwlist
server=/sequence.com/127.0.0.1#5335
ipset=/sequence.com/gfwlist
server=/serialssolutions.com/127.0.0.1#5335
@ -16032,6 +16094,8 @@ server=/servicetalk.io/127.0.0.1#5335
ipset=/servicetalk.io/gfwlist
server=/seselah.com/127.0.0.1#5335
ipset=/seselah.com/gfwlist
server=/setapp.com/127.0.0.1#5335
ipset=/setapp.com/gfwlist
server=/sextop1.net/127.0.0.1#5335
ipset=/sextop1.net/gfwlist
server=/sexzy4.com/127.0.0.1#5335
@ -16332,6 +16396,10 @@ server=/softether-download.com/127.0.0.1#5335
ipset=/softether-download.com/gfwlist
server=/softether.org/127.0.0.1#5335
ipset=/softether.org/gfwlist
server=/sohcradio.com/127.0.0.1#5335
ipset=/sohcradio.com/gfwlist
server=/sohfrance.org/127.0.0.1#5335
ipset=/sohfrance.org/gfwlist
server=/soirt4.fun/127.0.0.1#5335
ipset=/soirt4.fun/gfwlist
server=/solarcity.com/127.0.0.1#5335
@ -16608,10 +16676,6 @@ server=/sslpaypal.org/127.0.0.1#5335
ipset=/sslpaypal.org/gfwlist
server=/ssplive.pw/127.0.0.1#5335
ipset=/ssplive.pw/gfwlist
server=/ssrcloud.com/127.0.0.1#5335
ipset=/ssrcloud.com/gfwlist
server=/ssrcloud.org/127.0.0.1#5335
ipset=/ssrcloud.org/gfwlist
server=/ssrpass.pw/127.0.0.1#5335
ipset=/ssrpass.pw/gfwlist
server=/sstatic.net/127.0.0.1#5335
@ -18814,6 +18878,8 @@ server=/walmart.pharmacy/127.0.0.1#5335
ipset=/walmart.pharmacy/gfwlist
server=/walmartimages.com/127.0.0.1#5335
ipset=/walmartimages.com/gfwlist
server=/wanokokorosoh.com/127.0.0.1#5335
ipset=/wanokokorosoh.com/gfwlist
server=/wariolandshakeit.com/127.0.0.1#5335
ipset=/wariolandshakeit.com/gfwlist
server=/wariowarediy.com/127.0.0.1#5335
@ -19370,6 +19436,8 @@ server=/xn--czrs0t4phtr3a.cn/127.0.0.1#5335
ipset=/xn--czrs0t4phtr3a.cn/gfwlist
server=/xn--d1acpjx3f.xn--p1ai/127.0.0.1#5335
ipset=/xn--d1acpjx3f.xn--p1ai/gfwlist
server=/xn--d4ty0ojsqzfd.com/127.0.0.1#5335
ipset=/xn--d4ty0ojsqzfd.com/gfwlist
server=/xn--fiqs8sxootzz.cn/127.0.0.1#5335
ipset=/xn--fiqs8sxootzz.cn/gfwlist
server=/xn--fiqs8sxootzz.xn--hxt814e/127.0.0.1#5335
@ -19450,6 +19518,8 @@ server=/xposed.info/127.0.0.1#5335
ipset=/xposed.info/gfwlist
server=/xscale.com/127.0.0.1#5335
ipset=/xscale.com/gfwlist
server=/xtube.com/127.0.0.1#5335
ipset=/xtube.com/gfwlist
server=/xvideos-cdn.com/127.0.0.1#5335
ipset=/xvideos-cdn.com/gfwlist
server=/xvideos.com/127.0.0.1#5335
@ -19756,6 +19826,8 @@ server=/youav.com/127.0.0.1#5335
ipset=/youav.com/gfwlist
server=/youjizz.com/127.0.0.1#5335
ipset=/youjizz.com/gfwlist
server=/youlucky.com/127.0.0.1#5335
ipset=/youlucky.com/gfwlist
server=/youporn.com/127.0.0.1#5335
ipset=/youporn.com/gfwlist
server=/yourfantasybeginsnow.com/127.0.0.1#5335

View File

@ -11,19 +11,19 @@ set firewall.shadowsocksr.path=/var/etc/shadowsocksr.include
set firewall.shadowsocksr.reload=1
commit firewall
EOF
rm -rf /etc/config/shadowsocksr-opkg /etc/ssr/*opkg
touch /etc/ssr/china_ssr.txt
touch /etc/ssr/deny.list
touch /etc/ssr/white.list
touch /etc/ssr/black.list
touch /etc/ssr/netflix.list
touch /etc/ssr/netflixip.list
touch /etc/ssr/gfw_base.conf
touch /etc/ssr/gfw_list.conf
touch /etc/ssr/oversea_list.conf
touch /etc/ssr/ad.conf
rm -rf /etc/config/shadowsocksr-opkg /etc/ssrplus/*opkg
touch /etc/ssrplus/china_ssr.txt
touch /etc/ssrplus/deny.list
touch /etc/ssrplus/white.list
touch /etc/ssrplus/black.list
touch /etc/ssrplus/netflix.list
touch /etc/ssrplus/netflixip.list
touch /etc/ssrplus/gfw_base.conf
touch /etc/ssrplus/gfw_list.conf
touch /etc/ssrplus/oversea_list.conf
touch /etc/ssrplus/ad.conf
touch /etc/config/shadowsocksr
if [ ! -s "/etc/config/shadowsocksr" ] ; then
if [ ! -s "/etc/config/shadowsocksr" ]; then
uci -q batch <<-EOF >/dev/null
add shadowsocksr global
set shadowsocksr.@global[0].global_server='nil'
@ -42,7 +42,7 @@ set shadowsocksr.@global[0].switch_try_count='3'
set shadowsocksr.@global[0].gfwlist_url='https://cdn.jsdelivr.net/gh/v2fly/domain-list-community@release/gfwlist.txt'
set shadowsocksr.@global[0].chnroute_url='https://ispip.clang.cn/all_cn.txt'
set shadowsocksr.@global[0].nfip_url='https://cdn.jsdelivr.net/gh/QiuSimons/Netflix_IP/getflix.txt'
set shadowsocksr.@global[0].adblock_url='https://anti-ad.net/anti-ad-for-dnsmasq.conf'
set shadowsocksr.@global[0].adblock_url='https://neodev.team/lite_host_dnsmasq.conf'
add shadowsocksr server_subscribe
set shadowsocksr.@server_subscribe[0].proxy='0'
set shadowsocksr.@server_subscribe[0].auto_update_time='2'

View File

@ -1,10 +0,0 @@
#!/bin/sh -e
if [ -f /tmp/ssr-update.$1 ]; then
if (grep -wq "address=" /tmp/ssr-update.$1); then
return $?
else
cp /tmp/ssr-update.$1 /tmp/adnew.conf
cat /tmp/adnew.conf | grep ^\|\|[^\*]*\^$ | sed -e 's:||:address\=\/:' -e 's:\^:/0\.0\.0\.0:' >/tmp/ssr-update.$1
rm -f /tmp/adnew.conf
fi
fi

View File

@ -1,23 +0,0 @@
#!/bin/sh -e
generate_china_banned() {
cat $1 | base64 -d >/tmp/gfwlist.txt
rm -f $1
sed -i '/^@@|/d' /tmp/gfwlist.txt
cat /tmp/gfwlist.txt | sort -u |
sed 's#!.\+##; s#|##g; s#@##g; s#http:\/\/##; s#https:\/\/##;' |
sed '/\*/d; /apple\.com/d; /sina\.cn/d; /sina\.com\.cn/d; /baidu\.com/d; /byr\.cn/d; /jlike\.com/d; /weibo\.com/d; /zhongsou\.com/d; /youdao\.com/d; /sogou\.com/d; /so\.com/d; /soso\.com/d; /aliyun\.com/d; /taobao\.com/d; /jd\.com/d; /qq\.com/d' |
sed '/^[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$/d' |
grep '^[0-9a-zA-Z\.-]\+$' | grep '\.' | sed 's#^\.\+##' | sort -u |
awk 'BEGIN { prev = "________"; } {
cur = $0;
if (index(cur, prev) == 1 && substr(cur, 1 + length(prev) ,1) == ".") {
} else {
print cur;
prev = cur;
}
}' | sort -u
}
generate_china_banned /tmp/ssr-update.$1 >/tmp/gfw.txt
rm -f /tmp/gfwlist.txt
sed '/.*/s/.*/server=\/&\/127.0.0.1#5335\nipset=\/&\/gfwlist/' /tmp/gfw.txt >/tmp/ssr-update.$1
rm -f /tmp/gfw.txt

View File

@ -6,66 +6,36 @@
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
. $IPKG_INSTROOT/etc/init.d/shadowsocksr
LOCK_FILE="/var/lock/ssr-monitor.lock"
[ -f "$LOCK_FILE" ] && exit 2
touch "$LOCK_FILE"
NAME=shadowsocksr
uci_get_by_name() {
local ret=$(uci get $NAME.$1.$2 2>/dev/null)
echo ${ret:=$3}
}
uci_get_by_type() {
local ret=$(uci get $NAME.@$1[0].$2 2>/dev/null)
echo ${ret:=$3}
}
get_host_ip() {
local host=$1
local isip=""
local ip=$host
isip=$(echo $host | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}")
if [ -z "$isip" ]; then
if [ "$host" != "${host#*:[0-9a-fA-F]}" ]; then
ip=$host
else
local ip=$(resolveip -4 -t 3 $host | awk 'NR==1{print}')
# local hostip=$(ping $host -W 1 -s 1 -c 1 | grep PING | cut -d'(' -f 2 | cut -d')' -f1)
[ -z "$ip" ] && ip=$(wget -q -O- http://119.29.29.29/d?dn=$1 | awk -F ';' '{print $1}')
fi
fi
echo ${ip:="ERROR"}
}
server_process_count=$1
redir_tcp_process=$2
redir_udp_process=$3
tunnel_process=$4
kcp_process=$5
local_process=$6
pdnsd_process=$7
kcp_process=$4
local_process=$5
pdnsd_process=$6
if [ -z "$pdnsd_process" ]; then
pdnsd_process=0
fi
i=0
GLOBAL_SERVER=$(uci_get_by_type global global_server)
server=$(get_host_ip $(uci_get_by_name $GLOBAL_SERVER server))
[ "$server" == "ERROR" ] && hostip=$(uci_get_by_name $GLOBAL_SERVER ip)
lkcp_port=$(uci_get_by_name $GLOBAL_SERVER kcp_port)
server=$(uci_get_by_name $GLOBAL_SERVER server)
kcp_port=$(uci_get_by_name $GLOBAL_SERVER kcp_port)
server_port=$(uci_get_by_name $GLOBAL_SERVER server_port)
password=$(uci_get_by_name $GLOBAL_SERVER kcp_password)
kcp_param=$(uci_get_by_name $GLOBAL_SERVER kcp_param)
[ "$password" != "" ] && password="--key "${password}
while [ "1" == "1" ]; do #死循环
sleep 30s
sleep 000030s
#redir tcp
if [ "$redir_tcp_process" -gt 0 ]; then
icount=$(busybox ps -w | grep ssr-retcp | grep -v grep | wc -l)
if [ "$icount" == 0 ]; then
logger -t "$NAME" "ssr redir tcp error.restart!"
echo "$(date "+%Y-%m-%d %H:%M:%S") ssr redir tcp error.restart!" >>/tmp/ssrplus.log
echolog "ssr redir tcp error.restart!"
/etc/init.d/shadowsocksr restart
exit 0
fi
@ -75,17 +45,7 @@ while [ "1" == "1" ]; do #死循环
icount=$(busybox ps -w | grep ssr-reudp | grep -v grep | wc -l)
if [ "$icount" == 0 ]; then
logger -t "$NAME" "ssr redir udp error.restart!"
echo "$(date "+%Y-%m-%d %H:%M:%S") ssr redir udp error.restart!" >>/tmp/ssrplus.log
/etc/init.d/shadowsocksr restart
exit 0
fi
fi
#tunnel
if [ "$tunnel_process" -gt 0 ]; then
icount=$(busybox ps -w | grep ssr-tunnel | grep -v grep | wc -l)
if [ "$icount" == 0 ]; then
logger -t "$NAME" "ssr tunnel error.restart!"
echo "$(date "+%Y-%m-%d %H:%M:%S") ssr tunnel error.restart!" >>/tmp/ssrplus.log
echolog "ssr redir udp error.restart!"
/etc/init.d/shadowsocksr restart
exit 0
fi
@ -95,7 +55,7 @@ while [ "1" == "1" ]; do #死循环
icount=$(busybox ps -w | grep ssr-server | grep -v grep | wc -l)
if [ "$icount" -lt "$server_process_count" ]; then #如果进程挂掉就重启它
logger -t "$NAME" "ssr server error.restart!"
echo "$(date "+%Y-%m-%d %H:%M:%S") ssr server error.restart!" >>/tmp/ssrplus.log
echolog "ssr server error.restart!"
kill -9 $(busybox ps -w | grep ssr-server | grep -v grep | awk '{print $1}') >/dev/null 2>&1
/etc/init.d/shadowsocksr restart
exit 0
@ -106,7 +66,7 @@ while [ "1" == "1" ]; do #死循环
icount=$(busybox ps -w | grep kcptun-client | grep -v grep | wc -l)
if [ "$icount" -lt "$kcp_process" ]; then #如果进程挂掉就重启它
logger -t "$NAME" "ssr kcptun error.restart!"
echo "$(date "+%Y-%m-%d %H:%M:%S") ssr kcptun error.restart!" >>/tmp/ssrplus.log
echolog "ssr kcptun error.restart!"
killall -q -9 kcptun-client
(/usr/bin/kcptun-client -r $server:$kcp_port -l :$server_port $password $kcp_param &)
fi
@ -116,7 +76,7 @@ while [ "1" == "1" ]; do #死循环
icount=$(busybox ps -w | grep ssr-local | grep -v grep | wc -l)
if [ "$icount" -lt "$local_process" ]; then #如果进程挂掉就重启它
logger -t "$NAME" "global socks server error.restart!"
echo "$(date "+%Y-%m-%d %H:%M:%S") global socks server error.restart!" >>/tmp/ssrplus.log
echolog "global socks server error.restart!"
kill -9 $(busybox ps -w | grep ssr-local | grep -v grep | awk '{print $1}') >/dev/null 2>&1
/etc/init.d/shadowsocksr restart
exit 0
@ -124,31 +84,31 @@ while [ "1" == "1" ]; do #死循环
fi
#pdnsd
if [ "$pdnsd_process" -eq 1 ]; then
icount=$(busybox ps -w | grep pdnsd | grep -v grep | wc -l)
icount=$(busybox ps -w | grep $TMP_BIN_PATH/pdnsd | grep -v grep | wc -l)
if [ "$icount" -lt "$pdnsd_process" ]; then #如果进程挂掉就重启它
logger -t "$NAME" "pdnsd tunnel error.restart!"
echo "$(date "+%Y-%m-%d %H:%M:%S") pdnsd tunnel error.restart!" >>/tmp/ssrplus.log
echolog "pdnsd tunnel error.restart!"
if [ -f /var/run/pdnsd.pid ]; then
kill $(cat /var/run/pdnsd.pid) >/dev/null 2>&1
else
kill -9 $(ps | grep pdnsd | grep -v grep | awk '{print $1}') >/dev/null 2>&1
kill -9 $(ps | grep $TMP_PATH/pdnsd.conf | grep -v grep | awk '{print $1}') >/dev/null 2>&1
fi
(/usr/sbin/pdnsd -c /var/etc/pdnsd.conf >/dev/null 2>&1 &)
ln_start_bin $(first_type pdnsd) pdnsd -c $TMP_PATH/pdnsd.conf
fi
fi
#dns2socks
if [ "$pdnsd_process" -eq 2 ]; then
icount=$(busybox ps -w | grep -e ssr-dns -e dns2socks | grep -v grep | wc -l)
icount=$(busybox ps -w | grep -e ssrplus-dns -e "dns2socks 127.0.0.1 $tmp_dns_port" | grep -v grep | wc -l)
if [ "$icount" -lt 2 ]; then #如果进程挂掉就重启它
logger -t "$NAME" "dns2socks $dnsstr tunnel error.restart!"
echo "$(date "+%Y-%m-%d %H:%M:%S") dns2socks $dnsstr tunnel error.restart!" >>/tmp/ssrplus.log
echolog "dns2socks $dnsstr tunnel error.restart!"
dnsstr=$(uci_get_by_type global tunnel_forward 8.8.4.4:53)
dnsserver=$(echo "$dnsstr" | awk -F ':' '{print $1}')
dnsport=$(echo "$dnsstr" | awk -F ':' '{print $2}')
killall -q -9 dns2socks
kill -9 $(busybox ps -w | grep ssr-dns | grep -v grep | awk '{print $1}') >/dev/null 2>&1
microsocks -i 127.0.0.1 -p 10802 ssr-dns >/dev/null 2>&1 &
dns2socks 127.0.0.1:10802 $dnsserver:$dnsport 127.0.0.1:5335 -q >/dev/null 2>&1 &
kill -9 $(busybox ps -w | grep ssrplus-dns | grep -v grep | awk '{print $1}') >/dev/null 2>&1
kill -9 $(busybox ps -w | grep "dns2socks 127.0.0.1 $tmp_dns_port" | grep -v grep | awk '{print $1}') >/dev/null 2>&1
ln_start_bin $(first_type microsocks) microsocks -i 127.0.0.1 -p $tmp_dns_port ssrplus-dns
ln_start_bin $(first_type dns2socks) dns2socks 127.0.0.1:$tmp_dns_port $dnsserver:$dnsport 127.0.0.1:5335 -q
fi
fi
done

View File

@ -10,43 +10,43 @@ TAG="_SS_SPEC_RULE_" # comment tag
IPT="iptables -t nat" # alias of iptables
FWI=$(uci get firewall.shadowsocksr.path 2>/dev/null) # firewall include file
usage() {
cat <<-EOF
Usage: ssr-rules [options]
cat <<-EOF
Usage: ssr-rules [options]
Valid options are:
Valid options are:
-s <server_ip> ip address of shadowsocksr remote server
-l <local_port> port number of shadowsocksr local server
-S <server_ip> ip address of shadowsocksr remote UDP server
-L <local_port> port number of shadowsocksr local UDP server
-i <ip_list_file> a file content is bypassed ip list
-a <lan_ips> lan ip of access control, need a prefix to
define access control mode
-b <wan_ips> wan ip of will be bypassed
-w <wan_ips> wan ip of will be forwarded
-B <bp_lan_ips> lan ip of will be bypassed proxy
-p <fp_lan_ips> lan ip of will be global proxy
-G <gm_lan_ips> lan ip of will be game mode proxy
-D <proxy_ports> proxy ports
-F netflix mode
-N netflix server IP
-M netflix proxy mode
-m <Interface> Interface name
-I <ip_list_file> a file content is bypassed netflix ip list
-e <extra_options> extra options for iptables
-o apply the rules to the OUTPUT chain
-O apply the global rules to the OUTPUT chain
-u enable udprelay mode, TPROXY is required
-U enable udprelay mode, using different IP
and ports for TCP and UDP
-f flush the rules
-g gfwlist mode
-r router mode
-c oversea mode
-z all mode
-h show this help message and exit
EOF
exit $1
-s <server_ip> ip address of shadowsocksr remote server
-l <local_port> port number of shadowsocksr local server
-S <server_ip> ip address of shadowsocksr remote UDP server
-L <local_port> port number of shadowsocksr local UDP server
-i <ip_list_file> a file content is bypassed ip list
-a <lan_ips> lan ip of access control, need a prefix to
define access control mode
-b <wan_ips> wan ip of will be bypassed
-w <wan_ips> wan ip of will be forwarded
-B <bp_lan_ips> lan ip of will be bypassed proxy
-p <fp_lan_ips> lan ip of will be global proxy
-G <gm_lan_ips> lan ip of will be game mode proxy
-D <proxy_ports> proxy ports
-F shunt mode
-N shunt server IP
-M shunt proxy mode
-m <Interface> Interface name
-I <ip_list_file> a file content is bypassed shunt ip list
-e <extra_options> extra options for iptables
-o apply the rules to the OUTPUT chain
-O apply the global rules to the OUTPUT chain
-u enable udprelay mode, TPROXY is required
-U enable udprelay mode, using different IP
and ports for TCP and UDP
-f flush the rules
-g gfwlist mode
-r router mode
-c oversea mode
-z all mode
-h show this help message and exit
EOF
exit $1
}
loger() {
@ -94,15 +94,15 @@ ipset_r() {
$(gen_spec_iplist | sed -e "s/^/add ss_spec_wan_ac /")
EOF
$IPT -A SS_SPEC_WAN_AC -m set --match-set ss_spec_wan_ac dst -j RETURN
$IPT -A SS_SPEC_WAN_AC -m set --match-set gmlan src -m set ! --match-set china dst -j SS_SPEC_WAN_FW
$IPT -A SS_SPEC_WAN_AC -m set --match-set china dst -j RETURN
$IPT -A SS_SPEC_WAN_AC -m set --match-set gmlan src -m set ! --match-set china dst -j SS_SPEC_WAN_FW
$IPT -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW
;;
gfw)
ipset -N gfwlist hash:net 2>/dev/null
$IPT -A SS_SPEC_WAN_AC -m set --match-set china dst -j RETURN
$IPT -A SS_SPEC_WAN_AC -m set --match-set gfwlist dst -j SS_SPEC_WAN_FW
$IPT -A SS_SPEC_WAN_AC -m set --match-set gmlan src -m set ! --match-set china dst -j SS_SPEC_WAN_FW
$IPT -A SS_SPEC_WAN_AC -m set --match-set china dst -j RETURN
;;
oversea)
ipset -N oversea hash:net 2>/dev/null
@ -129,23 +129,24 @@ ipset_r() {
fi
for ip in $WAN_BP_IP; do ipset -! add whitelist $ip; done
for ip in $WAN_FW_IP; do ipset -! add blacklist $ip; done
if [ "$NETFLIX" != "0" ]; then
if [ "$SHUNT_PORT" != "0" ]; then
ipset -N netflix hash:net 2>/dev/null
for ip in $(cat ${NETFLIX_LIST:=/dev/null} 2>/dev/null); do ipset -! add netflix $ip; done
for ip in $(cat ${SHUNT_LIST:=/dev/null} 2>/dev/null); do ipset -! add netflix $ip; done
case "$SHUNT_PORT" in
0) ;;
1)
$IPT -I SS_SPEC_WAN_AC -p tcp -m set --match-set netflix dst -j REDIRECT --to-ports $local_port
;;
*)
$IPT -I SS_SPEC_WAN_AC -p tcp -m set --match-set netflix dst -j REDIRECT --to-ports $SHUNT_PORT
if [ "$SHUNT_PROXY" == "1" ]; then
$IPT -I SS_SPEC_WAN_AC -p tcp -d $SHUNT_IP -j REDIRECT --to-ports $local_port
else
ipset -! add whitelist $SHUNT_IP
fi
;;
esac
fi
case "$NETFLIX" in
1)
$IPT -I SS_SPEC_WAN_AC -p tcp -m set --match-set netflix dst -j REDIRECT --to-ports 4321
if [ "$NETFLIX_PROXY" == "1" ]; then
$IPT -I SS_SPEC_WAN_AC -p tcp -d $NETFLIX_IP -j REDIRECT --to-ports $local_port
else
ipset -! add whitelist $NETFLIX_IP
fi
;;
2)
$IPT -I SS_SPEC_WAN_AC -p tcp -m set --match-set netflix dst -j REDIRECT --to-ports $local_port
;;
esac
return $?
}
@ -159,8 +160,7 @@ fw_rule() {
$IPT -A SS_SPEC_WAN_FW -d 192.168.0.0/16 -j RETURN
$IPT -A SS_SPEC_WAN_FW -d 224.0.0.0/4 -j RETURN
$IPT -A SS_SPEC_WAN_FW -d 240.0.0.0/4 -j RETURN
$IPT -A SS_SPEC_WAN_FW -p tcp $PROXY_PORTS \
-j REDIRECT --to-ports $local_port 2>/dev/null || {
$IPT -A SS_SPEC_WAN_FW -p tcp $PROXY_PORTS -j REDIRECT --to-ports $local_port 2>/dev/null || {
loger 3 "Can't redirect, please check the iptables."
exit 1
}
@ -189,24 +189,23 @@ ac_rule() {
if [ -z "$Interface" ]; then
$IPT -I PREROUTING 1 -p tcp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_WAN_AC
else
for name in $Interface;do
local IFNAME=$(uci -P /var/state get network.$name.ifname 2>/dev/null)
[ -n "$IFNAME" ] && $IPT -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p tcp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_WAN_AC
for name in $Interface; do
local IFNAME=$(uci -P /var/state get network.$name.ifname 2>/dev/null)
[ -n "$IFNAME" ] && $IPT -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p tcp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_WAN_AC
done
fi
case "$OUTPUT" in
1)
$IPT -I OUTPUT 1 -p tcp $EXT_ARGS \
-m comment --comment "$TAG" -j SS_SPEC_WAN_AC
$IPT -I OUTPUT 1 -p tcp $EXT_ARGS -m comment --comment "$TAG" -j SS_SPEC_WAN_AC
;;
2)
ipset -! -R <<-EOF || return 1
create ssr_gen_router hash:net
$(gen_spec_iplist | sed -e "s/^/add ssr_gen_router /")
EOF
$IPT -N SS_SPEC_ROUTER && \
$IPT -A SS_SPEC_ROUTER -m set --match-set ssr_gen_router dst -j RETURN && \
$IPT -N SS_SPEC_ROUTER
$IPT -A SS_SPEC_ROUTER -m set --match-set ssr_gen_router dst -j RETURN
$IPT -A SS_SPEC_ROUTER -j SS_SPEC_WAN_FW
$IPT -I OUTPUT 1 -p tcp -m comment --comment "$TAG" -j SS_SPEC_ROUTER
;;
@ -234,25 +233,23 @@ tp_rule() {
$ipt -A SS_SPEC_TPROXY -p udp ! --dport 53 -d $SERVER -j RETURN
[ "$server" != "$SERVER" ] && ipset -! add whitelist $SERVER
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set bplan src -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set fplan src \
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set fplan src -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
case "$RUNMODE" in
router)
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set ! --match-set china dst \
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set ! --match-set ss_spec_wan_ac dst \
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
$IPT -A SS_SPEC_TPROXY -p udp -m set --match-set ss_spec_wan_ac dst -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set china dst -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set ! --match-set china dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set ! --match-set ss_spec_wan_ac dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
;;
gfw)
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set china dst -j RETURN
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set ! --match-set china dst \
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
$ipt -A SS_SPEC_TPROXY -p udp -m set $PROXY_PORTS --match-set gfwlist dst \
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set gfwlist dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set ! --match-set china dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
;;
oversea)
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set china dst \
-j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set oversea src -m dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set china dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
;;
all)
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
@ -261,9 +258,9 @@ tp_rule() {
if [ -z "$Interface" ]; then
$ipt -I PREROUTING 1 -p udp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_TPROXY
else
for name in $Interface;do
local IFNAME=$(uci -P /var/state get network.$name.ifname 2>/dev/null)
[ -n "$IFNAME" ] && $ipt -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p udp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_TPROXY
for name in $Interface; do
local IFNAME=$(uci -P /var/state get network.$name.ifname 2>/dev/null)
[ -n "$IFNAME" ] && $ipt -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p udp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_TPROXY
done
fi
return $?
@ -303,8 +300,7 @@ gen_include() {
[ -n "$FWI" ] || return 0
extract_rules() {
echo "*$1"
iptables-save -t $1 | grep SS_SPEC_ | \
sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/"
iptables-save -t $1 | grep SS_SPEC_ | sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/"
echo 'COMMIT'
}
cat <<-EOF >>$FWI
@ -362,16 +358,16 @@ while getopts ":m:s:l:S:L:i:e:a:B:b:w:p:G:D:F:N:M:I:oOuUfgrczh" arg; do
PROXY_PORTS=$OPTARG
;;
F)
NETFLIX=$OPTARG
SHUNT_PORT=$OPTARG
;;
N)
NETFLIX_IP=$OPTARG
SHUNT_IP=$OPTARG
;;
M)
NETFLIX_PROXY=$OPTARG
SHUNT_PROXY=$OPTARG
;;
I)
NETFLIX_LIST=$OPTARG
SHUNT_LIST=$OPTARG
;;
o)
OUTPUT=1
@ -409,16 +405,16 @@ if [ -z "$server" -o -z "$local_port" ]; then
usage 2
fi
case "$TPROXY" in
1)
SERVER=$server
LOCAL_PORT=$local_port
;;
2)
: ${SERVER:?"You must assign an ip for the udp relay server."}
: ${LOCAL_PORT:?"You must assign a port for the udp relay server."}
case "$TPROXY" in
1)
SERVER=$server
LOCAL_PORT=$local_port
;;
esac
2)
: ${SERVER:?"You must assign an ip for the udp relay server."}
: ${LOCAL_PORT:?"You must assign a port for the udp relay server."}
;;
esac
flush_r && fw_rule && ipset_r && ac_rule && tp_rule && gen_include
RET=$?

View File

@ -7,51 +7,23 @@
# See /LICENSE for more information.
#
. $IPKG_INSTROOT/etc/init.d/shadowsocksr
LOCK_FILE="/var/lock/ssr-switch.lock"
[ -f "$LOCK_FILE" ] && exit 2
touch "$LOCK_FILE"
LOG_FILE=/var/log/ssrplus.log
cycle_time=60
switch_time=3
normal_flag=0
server_locate=0
server_count=0
NAME=shadowsocksr
ENABLE_SERVER=nil
CONFIG_SWTICH_FILE=/var/etc/${NAME}_t.json
[ -n "$1" ] && cycle_time=$1
[ -n "$2" ] && switch_time=$2
uci_get_by_name() {
local ret=$(uci get $NAME.$1.$2 2>/dev/null)
echo ${ret:=$3}
}
uci_get_by_type() {
local ret=$(uci get $NAME.@$1[0].$2 2>/dev/null)
echo ${ret:=$3}
}
DEFAULT_SERVER=$(uci_get_by_type global global_server)
CURRENT_SERVER=$DEFAULT_SERVER
#解析ip
get_host_ip() {
local host=$1
local isip=""
local ip=$host
isip=$(echo $host | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}")
if [ -z "$isip" ]; then
if [ "$host" != "${host#*:[0-9a-fA-F]}" ]; then
ip=$host
else
local ip=$(resolveip -4 -t 3 $host | awk 'NR==1{print}')
[ -z "$ip" ] && ip=$(wget -q -O- http://119.29.29.29/d?dn=$1 | awk -F ';' '{print $1}')
fi
fi
echo ${ip:="ERROR"}
}
#判断代理是否正常
check_proxy() {
local result=0
@ -59,11 +31,11 @@ check_proxy() {
for i in $(seq 1 $try_count); do
/usr/bin/ssr-check www.google.com 80 $switch_time 1
if [ "$?" == "0" ]; then
# echo "$(date "+%Y-%m-%d %H:%M:%S") Check Google Proxy Success, count=$i" >> /tmp/ssrplus.log
# echolog "Check Google Proxy Success, count=$i"
result=0
break
else
# echo "$(date "+%Y-%m-%d %H:%M:%S") Check Google Proxy Fail, count=$i" >> /tmp/ssrplus.log
# echolog "Check Google Proxy Fail, count=$i"
/usr/bin/ssr-check www.baidu.com 80 $switch_time 1
if [ "$?" == "0" ]; then
result=1
@ -77,19 +49,18 @@ check_proxy() {
}
test_proxy() {
local servername=$(get_host_ip $(uci_get_by_name $1 server))
local servername=$(uci_get_by_name $1 server)
local serverport=$(uci_get_by_name $1 server_port)
ipset add ss_spec_wan_ac $servername 2>/dev/null
ret1=$?
ipset add whitelist $servername 2>/dev/null
tcping -q -c 3 -i 1 -t 2 -p $serverport $servername
if [ "$?" -gt "0" ]; then
[ "$ret1" == "0" ] && ipset del ss_spec_wan_ac $servername 2>/dev/null
ipset del whitelist $servername 2>/dev/null
return 1
fi
/usr/bin/ssr-check $servername $serverport $switch_time
local ret2=$?
[ "$ret1" == "0" ] && ipset del ss_spec_wan_ac $servername 2>/dev/null
if [ "$ret2" == "0" ]; then
local ret=$?
ipset del whitelist $servername 2>/dev/null
if [ "$ret" == "0" ]; then
return 0
else
return 1
@ -102,14 +73,13 @@ search_proxy() {
[ "$(uci_get_by_name $1 switch_enable 0)" != "1" ] && return 1
[ $ENABLE_SERVER != nil ] && return 0
[ "$1" == "$CURRENT_SERVER" ] && return 0
local servername=$(get_host_ip $(uci_get_by_name $1 server))
local servername=$(uci_get_by_name $1 server)
local serverport=$(uci_get_by_name $1 server_port)
ipset add ss_spec_wan_ac $servername 2>/dev/null
ret=$?
ipset add whitelist $servername 2>/dev/null
/usr/bin/ssr-check $servername $serverport $switch_time
local ret2=$?
[ "$ret" == "0" ] && ipset del ss_spec_wan_ac $servername 2>/dev/null
if [ "$ret2" == "0" ]; then
local ret=$?
ipset del whitelist $servername 2>/dev/null
if [ "$ret" == "0" ]; then
server_locate=$server_count
ENABLE_SERVER=$1
return 0
@ -137,49 +107,49 @@ start() {
#不支持kcptun启用时的切换
[ $(uci_get_by_name $DEFAULT_SERVER kcp_enable) = "1" ] && return 1
while [ "1" == "1" ]; do #死循环
sleep $cycle_time
sleep 0000$cycle_time
LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
#判断当前代理是否为缺省服务器
if [ "$CURRENT_SERVER" != "$DEFAULT_SERVER" ]; then
#echo "not default proxy"
echo "$(date "+%Y-%m-%d %H:%M:%S") Current server is not default Main server, try to switch back." >>/tmp/ssrplus.log
echolog "Current server is not default Main server, try to switch back."
#检查缺省服务器是否正常
if test_proxy $DEFAULT_SERVER; then
#echo "switch to default proxy"
echo "$(date "+%Y-%m-%d %H:%M:%S") Main server is avilable." >>/tmp/ssrplus.log
echolog "Main server is avilable."
#缺省服务器正常,切换回来
CURRENT_SERVER=$DEFAULT_SERVER
switch_proxy $CURRENT_SERVER
echo "$(date "+%Y-%m-%d %H:%M:%S") switch to default "$(uci_get_by_name $CURRENT_SERVER alias)" proxy!" >>/tmp/ssrplus.log
echolog "switch to default "$(uci_get_by_name $CURRENT_SERVER alias)" proxy!"
else
echo "$(date "+%Y-%m-%d %H:%M:%S") Main server is NOT avilable.Continue using current server." >>/tmp/ssrplus.log
echolog "Main server is NOT avilable.Continue using current server."
fi
fi
#判断当前代理是否正常
#echo "$(date "+%Y-%m-%d %H:%M:%S") Start checking if the current server is available." >>/tmp/ssrplus.log
#echolog "Start checking if the current server is available."
check_proxy
current_ret=$?
if [ "$current_ret" == "1" ]; then
#当前代理错误,判断有无可用的服务器
#echo "current error"
echo "$(date "+%Y-%m-%d %H:%M:%S") Current server error, try to switch another server." >>/tmp/ssrplus.log
echolog "Current server error, try to switch another server."
select_proxy
if [ "$ENABLE_SERVER" != nil ]; then
#有其他服务器可用,进行切换
#echo $(uci_get_by_name $new_proxy server)
echo "$(date "+%Y-%m-%d %H:%M:%S") Another server is avilable, now switching server." >>/tmp/ssrplus.log
echolog "Another server is avilable, now switching server."
CURRENT_SERVER=$ENABLE_SERVER
switch_proxy $CURRENT_SERVER
normal_flag=1
echo "$(date "+%Y-%m-%d %H:%M:%S") Switch to "$(uci_get_by_name $CURRENT_SERVER alias)" proxy!" >>/tmp/ssrplus.log
echolog "Switch to "$(uci_get_by_name $CURRENT_SERVER alias)" proxy!"
else
switch_proxy $CURRENT_SERVER
normal_flag=1
echo "$(date "+%Y-%m-%d %H:%M:%S") Try restart current server." >>/tmp/ssrplus.log
echolog "Try restart current server."
fi
else
normal_flag=0
# echo "$(date "+%Y-%m-%d %H:%M:%S") ShadowsocksR No Problem." >>/tmp/ssrplus.log
# echolog "ShadowsocksR No Problem."
fi
done
}

View File

@ -3,5 +3,5 @@
ipset -! flush china 2>/dev/null
ipset -! -R <<-EOF || exit 1
create china hash:net
$(cat ${china_ip:=/etc/ssr/china_ssr.txt} | sed -e "s/^/add china /")
$(cat ${china_ip:=/etc/ssrplus/china_ssr.txt} | sed -e "s/^/add china /")
EOF

View File

@ -0,0 +1,171 @@
local ucursor = require"luci.model.uci".cursor()
local json = require "luci.jsonc"
local server_section = arg[1]
local proto = arg[2]
local local_port = arg[3] or "0"
local socks_port = arg[4] or "0"
local server = ucursor:get_all("shadowsocksr", server_section)
local Xray = {
log = {
-- error = "/var/ssrplus.log",
loglevel = "warning"
},
-- 传入连接
inbound = (local_port ~= "0") and {
port = tonumber(local_port),
protocol = "dokodemo-door",
settings = {network = proto, followRedirect = true},
sniffing = {enabled = true, destOverride = {"http", "tls"}}
} or nil,
-- 开启 socks 代理
inboundDetour = (proto == "tcp" and socks_port ~= "0") and {
{
protocol = "socks",
port = tonumber(socks_port),
settings = {auth = "noauth", udp = true}
}
} or nil,
-- 传出连接
outbound = {
protocol = server.type,
settings = {
vnext = {
{
address = server.server,
port = tonumber(server.server_port),
users = {
{
id = server.vmess_id,
alterId = (server.type == "vmess") and tonumber(server.alter_id) or nil,
security = (server.type == "vmess") and server.security or nil,
encryption = (server.type == "vless") and server.vless_encryption or nil,
flow = (server.xtls == '1') and (server.vless_flow and server.vless_flow or "xtls-rprx-splice") or nil
}
}
}
}
},
-- 底层传输配置
streamSettings = {
network = server.transport,
security = (server.xtls == '1') and "xtls" or (server.tls == '1') and "tls" or "none",
tlsSettings = (server.tls == '1' and (server.insecure == "1" or server.tls_host)) and {
allowInsecure = (server.insecure == "1") and true or nil,
serverName = server.tls_host
} or nil,
xtlsSettings = (server.xtls == '1' and (server.insecure == "1" or server.tls_host)) and {
allowInsecure = (server.insecure == "1") and true or nil,
serverName = server.tls_host
} or nil,
tcpSettings = (server.transport == "tcp" and server.tcp_guise == "http") and {
header = {
type = server.tcp_guise,
request = {
path = {server.http_path} or {"/"},
headers = {Host = {server.http_host} or {}}
}
}
} or nil,
kcpSettings = (server.transport == "kcp") and {
mtu = tonumber(server.mtu),
tti = tonumber(server.tti),
uplinkCapacity = tonumber(server.uplink_capacity),
downlinkCapacity = tonumber(server.downlink_capacity),
congestion = (server.congestion == "1") and true or false,
readBufferSize = tonumber(server.read_buffer_size),
writeBufferSize = tonumber(server.write_buffer_size),
header = {type = server.kcp_guise},
seed = server.seed or nil
} or nil,
wsSettings = (server.transport == "ws") and (server.ws_path or server.ws_host or server.tls_host) and {
path = server.ws_path,
headers = (server.ws_host or server.tls_host) and {
Host = server.ws_host or server.tls_host
} or nil
} or nil,
httpSettings = (server.transport == "h2") and {
path = server.h2_path or "",
host = {server.h2_host} or nil
} or nil,
quicSettings = (server.transport == "quic") and {
security = server.quic_security,
key = server.quic_key,
header = {type = server.quic_guise}
} or nil
},
mux = (server.mux == "1" and server.xtls ~= "1") and {
enabled = true,
concurrency = tonumber(server.concurrency)
} or nil
} or nil
}
local trojan = {
log_level = 3,
run_type = (proto == "nat" or proto == "tcp") and "nat" or "client",
local_addr = "0.0.0.0",
local_port = tonumber(local_port),
remote_addr = server.server,
remote_port = tonumber(server.server_port),
udp_timeout = 60,
-- 传入连接
password = {server.password},
-- 传出连接
ssl = {
verify = (server.insecure == "0") and true or false,
verify_hostname = (server.tls == "1") and true or false,
cert = "",
cipher = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA",
cipher_tls13 = "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384",
sni = server.tls_host,
alpn = {"h2", "http/1.1"},
curve = "",
reuse_session = true,
session_ticket = false
},
tcp = {
no_delay = true,
keep_alive = true,
reuse_port = true,
fast_open = (server.fast_open == "1") and true or false,
fast_open_qlen = 20
}
}
local naiveproxy = {
proxy = (server.username and server.password and server.server and server.server_port) and "https://" .. server.username .. ":" .. server.password .. "@" .. server.server .. ":" .. server.server_port,
listen = (proto == "redir") and "redir" .. "://0.0.0.0:" .. tonumber(local_port) or "socks" .. "://0.0.0.0:" .. tonumber(local_port),
concurrency = (socks_port ~= "0") and tonumber(socks_port) or "1"
}
local ss = {
server = (server.kcp_enable == "1") and "127.0.0.1" or server.server,
server_port = tonumber(server.server_port),
local_address = "0.0.0.0",
local_port = tonumber(local_port),
password = server.password,
method = server.encrypt_method,
timeout = tonumber(server.timeout) or 60,
fast_open = (server.fast_open == "1") and true or false,
reuse_port = true
}
if server.type == "ss" then
if server.plugin then
ss.plugin = server.plugin
ss.plugin_opts = (server.simple_obfs) and server.simple_obfs .. server.plugin_opts or (server.v2ray_plugin ~= "none") and server.v2ray_plugin .. server.plugin_opts or nil
end
print(json.stringify(ss, 1))
end
if server.type == "ssr" then
ss.protocol = server.protocol
ss.protocol_param = server.protocol_param
ss.obfs = server.obfs
ss.obfs_param = server.obfs_param
print(json.stringify(ss, 1))
end
if server.type == "vless" or server.type == "vmess" then
print(json.stringify(Xray, 1))
end
if server.type == "trojan" then
print(json.stringify(trojan, 1))
end
if server.type == "naiveproxy" then
print(json.stringify(naiveproxy, 1))
end

View File

@ -1,72 +1,95 @@
#!/bin/sh
cat <<-EOF >$1
base {
argv1=$1
argv2=$2
argv3=$3
argv4=$4
argv5=$5
argv6=$6
argv7=$7
argv8=$8
argv9=$9
cat <<-EOF >$argv1
base {
log_debug = off;
log_info = off;
log = stderr;
daemon = on;
redirector = iptables;
reuseport = on;
}
}
EOF
if [ "$2" == "socks5" ]; then
if [ "$3" == "tcp" ]; then
if [ "$7" == "0" ]; then
cat <<-EOF >>$1
redsocks {
bind = "0.0.0.0:$4";
relay = "$5:$6";
type = socks5;
autoproxy = 0;
timeout = 10;
tcp() {
if [ "$argv7" == "0" ]; then
cat <<-EOF >>$argv1
redsocks {
bind = "0.0.0.0:$argv4";
relay = "$argv5:$argv6";
type = socks5;
autoproxy = 0;
timeout = 10;
}
EOF
else
cat <<-EOF >>$argv1
redsocks {
bind = "0.0.0.0:$argv4";
relay = "$argv5:$argv6";
type = socks5;
autoproxy = 0;
timeout = 10;
login = "$argv8";
password = "$argv9";
}
EOF
fi
}
EOF
else
cat <<-EOF >>$1
redsocks {
bind = "0.0.0.0:$4";
relay = "$5:$6";
type = socks5;
autoproxy = 0;
timeout = 10;
login = "$8";
password = "$9";
udp() {
if [ "$argv7" == "0" ]; then
cat <<-EOF >>$argv1
redudp {
bind = "0.0.0.0:$argv4";
relay = "$argv5:$argv6";
type = socks5;
udp_timeout = 10;
}
EOF
else
cat <<-EOF >>$argv1
redudp {
bind = "0.0.0.0:$argv4";
relay = "$argv5:$argv6";
type = socks5;
udp_timeout = 10;
login = "$argv8";
password = "$argv9";
}
EOF
fi
}
EOF
fi
else
if [ "$7" == "0" ]; then
cat <<-EOF >>$1
redudp {
bind = "0.0.0.0:$4";
relay = "$5:$6";
type = socks5;
udp_timeout = 10;
}
EOF
else
cat <<-EOF >>$1
redudp {
bind = "0.0.0.0:$4";
relay = "$5:$6";
type = socks5;
udp_timeout = 10;
login = "$8";
password = "$9";
}
EOF
fi
fi
else
cat <<-EOF >>$1
redsocks {
bind = "0.0.0.0:$4";
type = direct;
interface = $3;
autoproxy = 0;
timeout = 10;
}
EOF
fi
case "$argv2" in
socks5)
case "$argv3" in
tcp)
tcp
;;
udp)
udp
;;
*)
tcp
udp
;;
esac
;;
*)
cat <<-EOF >>$argv1
redsocks {
bind = "0.0.0.0:$argv4";
type = direct;
interface = $argv3;
autoproxy = 0;
timeout = 10;
}
EOF
;;
esac

View File

@ -1,40 +0,0 @@
local ucursor = require "luci.model.uci".cursor()
local json = require "luci.jsonc"
local server_section = arg[1]
local proto = arg[2]
local local_port = arg[3]
local server = ucursor:get_all("shadowsocksr", server_section)
local trojan = {
log_level = 3,
run_type = proto,
local_addr = "0.0.0.0",
local_port = tonumber(local_port),
remote_addr = server.server,
remote_port = tonumber(server.server_port),
udp_timeout = 60,
-- 传入连接
password = {server.password},
-- 传出连接
ssl = {
verify = (server.insecure == "0") and true or false,
verify_hostname = (server.tls == "1") and true or false,
cert = "",
cipher = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA",
cipher_tls13 = "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384",
sni = server.tls_host,
alpn = {"h2", "http/1.1"},
curve = "",
reuse_session = true,
session_ticket = false,
},
tcp = {
no_delay = true,
keep_alive = true,
reuse_port = true,
fast_open = (server.fast_open == "1") and true or false,
fast_open_qlen = 20
}
}
print(json.stringify(trojan, 1))

View File

@ -1,111 +0,0 @@
local ucursor = require "luci.model.uci".cursor()
local json = require "luci.jsonc"
local server_section = arg[1]
local proto = arg[2]
local local_port = arg[3] or "0"
local socks_port = arg[4] or "0"
local server = ucursor:get_all("shadowsocksr", server_section)
local Xray = {
log = {
-- error = "/var/ssrplus.log",
loglevel = "warning"
},
-- 传入连接
inbound = (local_port ~= "0") and {
port = local_port,
protocol = "dokodemo-door",
settings = {
network = proto,
followRedirect = true
},
sniffing = {
enabled = true,
destOverride = { "http", "tls" }
}
} or nil,
-- 开启 socks 代理
inboundDetour = (proto == "tcp" and socks_port ~= "0") and {
{
protocol = "socks",
port = socks_port,
settings = {
auth = "noauth",
udp = true
}
}
} or nil,
-- 传出连接
outbound = {
protocol = (server.type == "v2ray") and "vmess" or "vless",
settings = {
vnext = {
{
address = server.server,
port = tonumber(server.server_port),
users = {
{
id = server.vmess_id,
alterId = (server.type == "v2ray") and tonumber(server.alter_id) or nil,
security = (server.type == "v2ray") and server.security or nil,
encryption = (server.type == "vless") and server.vless_encryption or nil,
flow = (server.xtls == '1') and (server.vless_flow and server.vless_flow or "xtls-rprx-origin") or nil,
}
}
}
}
},
-- 底层传输配置
streamSettings = {
network = server.transport,
security = (server.tls == '1') and ((server.xtls == '1') and "xtls" or "tls") or "none",
tlsSettings = (server.tls == '1' and server.xtls ~= '1') and {allowInsecure = (server.insecure ~= "0") and true or nil,serverName=server.tls_host,} or nil,
xtlsSettings = (server.xtls == '1') and {allowInsecure = (server.insecure ~= "0") and true or nil,serverName=server.tls_host,} or nil,
tcpSettings = (server.transport == "tcp" and server.tcp_guise == "http") and {
header = {
type = server.tcp_guise,
request = {
path = {server.http_path} or {"/"},
headers = {
Host = {server.http_host} or {}
}
}
}
} or nil,
kcpSettings = (server.transport == "kcp") and {
mtu = tonumber(server.mtu),
tti = tonumber(server.tti),
uplinkCapacity = tonumber(server.uplink_capacity),
downlinkCapacity = tonumber(server.downlink_capacity),
congestion = (server.congestion == "1") and true or false,
readBufferSize = tonumber(server.read_buffer_size),
writeBufferSize = tonumber(server.write_buffer_size),
header = {
type = server.kcp_guise
},
seed = server.seed or nil
} or nil,
wsSettings = (server.transport == "ws") and (server.ws_path ~= nil or server.ws_host ~= nil) and {
path = server.ws_path,
headers = (server.ws_host ~= nil) and {
Host = server.ws_host
} or nil,
} or nil,
httpSettings = (server.transport == "h2") and {
path = server.h2_path or "",
host = {server.h2_host} or nil
} or nil,
quicSettings = (server.transport == "quic") and {
security = server.quic_security,
key = server.quic_key,
header = {
type = server.quic_guise
}
} or nil
},
mux = (server.xtls ~= "1") and {
enabled = (server.mux == "1") and true or false,
concurrency = tonumber(server.concurrency)
} or nil
} or nil
}
print(json.stringify(Xray,1))

View File

@ -1,61 +1,47 @@
#!/bin/sh
NAME=shadowsocksr
switch_server=$1
uci_get_by_type() {
local ret=$(uci get $NAME.@$1[0].$2 2>/dev/null)
echo ${ret:=$3}
. $IPKG_INSTROOT/etc/init.d/shadowsocksr
netflix() {
if [ -f "$TMP_DNSMASQ_PATH/gfw_list.conf" ]; then
for line in $(cat /etc/ssrplus/netflix.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_list.conf; done
for line in $(cat /etc/ssrplus/netflix.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_base.conf; done
fi
cat /etc/ssrplus/netflix.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$1\nipset=\/&\/netflix/" >$TMP_DNSMASQ_PATH/netflix_forward.conf
}
if [ -z "$switch_server" ]; then
GLOBAL_SERVER=$(uci_get_by_type global global_server nil)
else
GLOBAL_SERVER=$switch_server
fi
mkdir -p /tmp/dnsmasq.ssr
mkdir -p $TMP_DNSMASQ_PATH
if [ "$(uci_get_by_type global run_mode router)" == "oversea" ]; then
cp -rf /etc/ssr/oversea_list.conf /tmp/dnsmasq.ssr/
cp -rf /etc/ssrplus/oversea_list.conf $TMP_DNSMASQ_PATH/
else
cp -rf /etc/ssr/gfw_list.conf /tmp/dnsmasq.ssr/
cp -rf /etc/ssr/gfw_base.conf /tmp/dnsmasq.ssr/
cp -rf /etc/ssrplus/gfw_list.conf $TMP_DNSMASQ_PATH/
cp -rf /etc/ssrplus/gfw_base.conf $TMP_DNSMASQ_PATH/
fi
NETFLIX_SERVER=$(uci_get_by_type global netflix_server nil)
[ "$NETFLIX_SERVER" == "same" ] && NETFLIX_SERVER=$GLOBAL_SERVER
if [ "$NETFLIX_SERVER" != "nil" ]; then
netflix() {
if [ -f "/tmp/dnsmasq.ssr/gfw_list.conf" ]; then
for line in $(cat /etc/ssr/netflix.list); do sed -i "/$line/d" /tmp/dnsmasq.ssr/gfw_list.conf; done
for line in $(cat /etc/ssr/netflix.list); do sed -i "/$line/d" /tmp/dnsmasq.ssr/gfw_base.conf; done
fi
awk '!/^$/&&!/^#/{printf("ipset=/%s/'"netflix"'\n",$0)}' /etc/ssr/netflix.list >/tmp/dnsmasq.ssr/netflix_forward.conf
awk '!/^$/&&!/^#/{printf("server=/%s/'"127.0.0.1#$1"'\n",$0)}' /etc/ssr/netflix.list >>/tmp/dnsmasq.ssr/netflix_forward.conf
}
if [ "$NETFLIX_SERVER" != "$GLOBAL_SERVER" ]; then
netflix 5555
else
netflix 5335
fi
else
rm -f /tmp/dnsmasq.ssr/netflix_forward.conf
fi
for line in $(cat /etc/ssr/black.list); do sed -i "/$line/d" /tmp/dnsmasq.ssr/gfw_list.conf; done
for line in $(cat /etc/ssr/black.list); do sed -i "/$line/d" /tmp/dnsmasq.ssr/gfw_base.conf; done
for line in $(cat /etc/ssr/white.list); do sed -i "/$line/d" /tmp/dnsmasq.ssr/gfw_list.conf; done
for line in $(cat /etc/ssr/white.list); do sed -i "/$line/d" /tmp/dnsmasq.ssr/gfw_base.conf; done
for line in $(cat /etc/ssr/deny.list); do sed -i "/$line/d" /tmp/dnsmasq.ssr/gfw_list.conf; done
for line in $(cat /etc/ssr/deny.list); do sed -i "/$line/d" /tmp/dnsmasq.ssr/gfw_base.conf; done
awk '!/^$/&&!/^#/{printf("ipset=/%s/'"blacklist"'\n",$0)}' /etc/ssr/black.list >/tmp/dnsmasq.ssr/blacklist_forward.conf
awk '!/^$/&&!/^#/{printf("server=/%s/'"127.0.0.1#5335"'\n",$0)}' /etc/ssr/black.list >>/tmp/dnsmasq.ssr/blacklist_forward.conf
awk '!/^$/&&!/^#/{printf("ipset=/%s/'"whitelist"'\n",$0)}' /etc/ssr/white.list >/tmp/dnsmasq.ssr/whitelist_forward.conf
awk '!/^$/&&!/^#/{printf("address=/%s/''\n",$0)}' /etc/ssr/deny.list >/tmp/dnsmasq.ssr/denylist.conf
case "$(uci_get_by_type global netflix_server nil)" in
nil)
rm -f $TMP_DNSMASQ_PATH/netflix_forward.conf
;;
same)
netflix 5335
;;
*)
netflix $tmp_shunt_dns_port
;;
esac
for line in $(cat /etc/ssrplus/black.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_list.conf; done
for line in $(cat /etc/ssrplus/black.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_base.conf; done
for line in $(cat /etc/ssrplus/white.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_list.conf; done
for line in $(cat /etc/ssrplus/white.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_base.conf; done
for line in $(cat /etc/ssrplus/deny.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_list.conf; done
for line in $(cat /etc/ssrplus/deny.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_base.conf; done
cat /etc/ssrplus/black.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#5335\nipset=\/&\/blacklist/" >$TMP_DNSMASQ_PATH/blacklist_forward.conf
cat /etc/ssrplus/white.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1\nipset=\/&\/whitelist/" >$TMP_DNSMASQ_PATH/whitelist_forward.conf
cat /etc/ssrplus/deny.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/address=\/&\//" >$TMP_DNSMASQ_PATH/denylist.conf
if [ "$(uci_get_by_type global adblock 0)" == "1" ]; then
[ -z "$switch_server" ] && cp -f /etc/ssr/ad.conf /tmp/dnsmasq.ssr/
if [ -f "/tmp/dnsmasq.ssr/ad.conf" ]; then
for line in $(cat /etc/ssr/black.list); do sed -i "/$line/d" /tmp/dnsmasq.ssr/ad.conf; done
for line in $(cat /etc/ssr/white.list); do sed -i "/$line/d" /tmp/dnsmasq.ssr/ad.conf; done
for line in $(cat /etc/ssr/deny.list); do sed -i "/$line/d" /tmp/dnsmasq.ssr/ad.conf; done
for line in $(cat /etc/ssr/netflix.list); do sed -i "/$line/d" /tmp/dnsmasq.ssr/ad.conf; done
cp -f /etc/ssrplus/ad.conf $TMP_DNSMASQ_PATH/
if [ -f "$TMP_DNSMASQ_PATH/ad.conf" ]; then
for line in $(cat /etc/ssrplus/black.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/ad.conf; done
for line in $(cat /etc/ssrplus/white.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/ad.conf; done
for line in $(cat /etc/ssrplus/deny.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/ad.conf; done
for line in $(cat /etc/ssrplus/netflix.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/ad.conf; done
fi
else
rm -f /tmp/dnsmasq.ssr/ad.conf
rm -f $TMP_DNSMASQ_PATH/ad.conf
fi

View File

@ -1,6 +1,6 @@
#!/bin/sh
/usr/bin/lua /usr/share/shadowsocksr/update.lua
sleep 2s
/usr/share/shadowsocksr/chinaipset.sh /tmp/etc/china_ssr.txt
/usr/share/shadowsocksr/chinaipset.sh /var/etc/ssrplus/china_ssr.txt
sleep 2s
/usr/bin/lua /usr/share/shadowsocksr/subscribe.lua

Some files were not shown because too many files have changed in this diff Show More