treewide: add missing PKG_HASH / PKG_MIRROR_HASH

Signed-off-by: CN_SZTL <cnsztl@project-openwrt.eu.org>
This commit is contained in:
CN_SZTL 2020-12-26 22:15:11 +08:00
parent 5cc3fdf1ae
commit e29ea4c174
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
72 changed files with 57 additions and 2751 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

@ -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

@ -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,6 +10,7 @@ PKG_LICENSE_FILES:=LICENSE
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/rofl0r/microsocks.git
PKG_MIRROR_HASH:=94356d864ba39a978becc59cf5cacbc71fdd16c0c9c868aaef006ddf8742a944
PKG_SOURCE_VERSION:=be545814aeca1158ae38e2d6c66b1197679dab63
PKG_SOURCE_SUBDIR:=$(PKG_NAME)

View File

@ -6,6 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=n2n
PKG_SOURCE_URL:=https://github.com/ntop/n2n.git
PKG_MIRROR_HASH:=669435ba412025cef30460741982fe30a152990b26b275c4786bfc628261182d
PKG_SOURCE_VERSION:=99e56e9f3c34c49eeb297971d41150b433489120
PKG_VERSION:=2.8.0_git-$(PKG_SOURCE_VERSION)
PKG_RELEASE:=3

View File

@ -14,6 +14,7 @@ PKG_RELEASE:=1
PKG_SOURCE_DATE:=2019-10-21
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/Chion82/netfilter-full-cone-nat.git
PKG_MIRROR_HASH:=e7bb77d9916d190b3c02975faf62d1794557d4a4a774cd0cf1f3a6ddb7403e3e
PKG_SOURCE_VERSION:=0cf3b48fd7d2fa81d0297d1fff12bbd0580fc435
PKG_LICENSE:=GPL-2.0

View File

@ -15,7 +15,7 @@ PKG_USE_MIPS16:=0
PKG_SOURCE:=$(SRC_PKG_NAME)-$(PKG_VERSION)-gpl.tgz
PKG_SOURCE_URL:=https://polarssl.org/download/
PKG_MD5SUM:=66ebbbf67e8a9463041846822b0a1692
PKG_HASH:=f413146c177c52d4ad8f48015e2fb21dd3a029ca30a2ea000cbc4f9bd092c933
PKG_BUILD_DIR:=$(BUILD_DIR)/$(SRC_PKG_NAME)-$(PKG_VERSION)

View File

@ -6,6 +6,7 @@ PKG_RELEASE:=6
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/shadowsocksrr/shadowsocksr-libev
PKG_MIRROR_HASH:=834ce598f940fb9db2bf4c0d1f8cfa883df27c1c8553dbcb306d6f32ae13c317
PKG_SOURCE_VERSION:=d63ff863800a5645aca4309d5dd5962bd1e95543
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.xz

View File

@ -13,7 +13,7 @@ PKG_RELEASE:=8
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=@SF/socks-relay
PKG_MD5SUM:=8121ff3d1b741de95dd6eacc5ad5811c
PKG_HASH:=efa38cb3e9e745a05ccb4b59fcf5d041184f15dbea8eb80c1b0ce809bb00c924
include $(INCLUDE_DIR)/package.mk

View File

@ -6,6 +6,7 @@ PKG_RELEASE=$(PKG_SOURCE_VERSION)
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/coolsnowwolf/tcping
PKG_MIRROR_HASH:=223649603b5cc521a721d5825ab17f9bd95935e2d650a41f240110b3b4b8449e
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_VERSION:=d890cc1bd8e3951390ceeff1ccb092a5d802850c
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz

View File

@ -10,6 +10,7 @@ PKG_LICENSE_FILES:=LICENSE
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/Wind4/vlmcsd.git
PKG_MIRROR_HASH:=9510cf4e48e809b09fea60f29e41d3a0fb0f58257644dc60929ad503e8a156af
PKG_SOURCE_VERSION:=e599080486478e219cd065e141d6de050a450c27
PKG_SOURCE_SUBDIR:=$(PKG_NAME)

View File

@ -13,7 +13,7 @@ PKG_RELEASE:=7
PKG_SOURCE:=vsftpd-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://security.appspot.com/downloads/
PKG_MD5SUM:=da119d084bd3f98664636ea05b5bb398
PKG_HASH:=9d4d2bf6e6e2884852ba4e69e157a2cecd68c5a7635d66a3a8cf8d898c955ef7
PKG_LICENSE:=GPLv2
BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)

View File

@ -13,7 +13,7 @@ PKG_RELEASE:=1
PKG_SOURCE:=wxGTK-$(PKG_VERSION).tar.bz2
PKG_SOURCE_URL:=@SF/wxwindows
PKG_MD5SUM:=08f81ab60647308058f6ce99712b14f8
PKG_HASH:=f4193c29fb0e790c9a5c8936f082377a7e51c76bccafe41f4f9da7ca15c0ef1a
PKG_BUILD_DIR:=$(BUILD_DIR)/wxGTK-$(PKG_VERSION)

View File

@ -14,6 +14,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/aa65535/hev-dns-forwarder.git
PKG_MIRROR_HASH:=b2f104d563881be9e9f744d631af23fd0b1f088cd6813f90e3851bb0bd5aab1a
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE)
PKG_SOURCE_VERSION:=289e8c9c7167200668dff83b1e0cbce258665387
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz

View File

@ -7,7 +7,7 @@ PKG_RELEASE:=20200218
PKG_SOURCE:=SourceCode.zip
PKG_SOURCE_SUBDIR:=DNS2SOCKS
PKG_SOURCE_URL:=@SF/dns2socks
PKG_MD5SUM:=ec82de936ad004cc940502cd2a1bff5b
PKG_HASH:=406b5003523577d39da66767adfe54f7af9b701374363729386f32f6a3a995f4
PKG_MAINTAINER:=ghostmaker
PKG_LICENSE:=BSD-3-Clause

View File

@ -13,6 +13,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/zfl9/ipt2socks.git
PKG_MIRROR_HASH:=914465049c065c9fd21a6eabb96418acf134f2b6f0a1b861d5987ae0d10da83a
PKG_SOURCE_VERSION:=384dab4bae5ed9402e07ec1950e502c05812bc26
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz

View File

@ -14,6 +14,7 @@ PKG_RELEASE:=2
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/semigodking/redsocks.git
PKG_MIRROR_HASH:=938f859d1b55a91aa5cbcda3ddff1d04ccab292f784b0434060c73acab12c457
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_VERSION:=d94c245ea47859cda5b4b7373308589206b97bdc
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz

View File

@ -14,6 +14,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/Lienol/tcping.git
PKG_MIRROR_HASH:=79414cd8e1d124422a36b8fe36a1f296b7d9bde99807b2c90ad81bbd65e200e0
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_VERSION:=db9101834732dac9aaa59dbb7fb9c74612dbf723
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz

View File

@ -11,6 +11,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/p4gefau1t/trojan-go.git
PKG_MIRROR_HASH:=ccec21f4f2bac17abf41978221a0e5ed16d8c8135ae54a292fdeddce62fa9127
PKG_SOURCE_VERSION:=d051cf4c8852d708769ca1c4e514306a88da830b
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

@ -12,6 +12,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/peter-tank/trojan-plus.git
PKG_MIRROR_HASH:=ec9b474a708e9e92715e79d09163777cd539837ea41656190a75cffada91ee7a
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_VERSION:=a6394cdd718669b0c7491493a78e61f6f0f899b3
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz

View File

@ -12,6 +12,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/trojan-gfw/trojan.git
PKG_MIRROR_HASH:=2f7f60ae2ef6e57b9565b984df2e7b9560786ad0a63402e93523804f140e39ca
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_VERSION:=8606b7110fe79f8ab02d60c897f87ffb0a9b23f0
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz

View File

@ -13,6 +13,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/updateing/minieap.git
PKG_MIRROR_HASH:=41c976810c994d6125f6fe3cb1d978f7e257aa587ee32229364942a82621b273
PKG_REV=9e10f320a9fce46c8f4286324a0fc70e572f61dc
PKG_SOURCE_VERSION:=$(PKG_REV)

View File

@ -13,6 +13,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/betolj/ndpi-netfilter
PKG_MIRROR_HASH:=1a26ea73375a99831be6949faa9bc768e9f4f9e3594286729ec9e61dd6402b07
PKG_SOURCE_VERSION:=4875069df027199698ba0b7c53541ae2a4e3ba00
PKG_LICENSE:=GPL-2.0

View File

@ -9,6 +9,7 @@ PKG_SOURCE_PROTO:=git
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_URL:=https://github.com/dosgo/ngrok-c.git
PKG_MIRROR_HASH:=9534e2510de60d74e1b4e2aadb88309c1b0e5401998e3872a6c412832259408c
PKG_SOURCE_VERSION:=$(PKG_REV)
include $(INCLUDE_DIR)/package.mk

View File

@ -12,6 +12,7 @@ PKG_RELEASE:=bc9f540
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/1715173329/Pcap_DNSProxy.git
PKG_MIRROR_HASH:=74d86515f17a329fe586fc9acb0c0ff3494fd7f39e42ed50f442d247e3205556
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_VERSION:=bc9f540d9febc5f61d24d583dbdbcc858dd817e3
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz

View File

@ -14,6 +14,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/aa65535/ChinaDNS.git
PKG_MIRROR_HASH:=3d7063552dc5755163be52de7311fcbed87f67622bbf4c1b0dc748431b57091b
PKG_SOURCE_VERSION:=00616680114011553881760e843b77a3519e8fbf
PKG_SOURCE_SUBDIR:=ChinaDNS-$(PKG_VERSION)-$(PKG_SOURCE_VERSION)
PKG_SOURCE:=ChinaDNS-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.xz

View File

@ -6,6 +6,7 @@ PKG_RELEASE:=3
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/shadowsocksrr/pdnsd.git
PKG_MIRROR_HASH:=277bdc34c27377b7626d83a6625b6f07f0e7268d5e8d926152ffe9e5022cc338
PKG_SOURCE_VERSION:=a8e46ccba7b0fa2230d6c42ab6dcd92926f6c21d
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION)
PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.gz

View File

@ -14,7 +14,7 @@ PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/aa65535/openwrt-shadowvpn/releases/download/v$(PKG_VERSION)
PKG_MD5SUM:=b722b0e72608e00bca00bd4061e37209
PKG_HASH:=53b445cf47262f407bfef72d32bc6ca5d1341dcd3878ac2b4cd35ee030199dea
PKG_LICENSE:=GPLv3
PKG_LICENSE_FILES:=COPYING

View File

@ -13,6 +13,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/wangyu-/udp2raw-tunnel.git
PKG_MIRROR_HASH:=6b7c07f6277729ba8b3b287316b5b7a80c5b1e47258ecc8b1c8618be3aa73888
PKG_SOURCE_VERSION:=cc6ea766c495cf4c69d1c7485728ba022b0f19de
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.xz

View File

@ -12,11 +12,9 @@ PKG_VERSION := r11518-$(PKG_REV)
PKG_RELEASE := 1
PKG_SOURCE_PROTO:= git
#PKG_SOURCE_PROTO := svn
PKG_SOURCE_VERSION := $(PKG_REV)
PKG_SOURCE_URL := https://github.com/nx111/oscam.git
#PKG_SOURCE_URL := http://www.streamboard.tv/svn/oscam/trunk
#PKG_SOURCE_URL := http://www.oscam.cc/svn/oscam-mirror/trunk
PKG_MIRROR_HASH := 6873d24637f14ec695e32a8a91f17bd8ef38d615a55fb76e6440f6a37da906e3
PKG_LICENSE := GPL-3.0
PKG_LICENSE_FILE := COPYING

View File

@ -13,6 +13,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/scutclient/scutclient.git
PKG_MIRROR_HASH:=31e598d418956e236de287834f9553026f7853b06c5ba091fbf6f933bc305116
PKG_REV:=10d0c13cc5902925c479f1d50a68564c3129aebc
PKG_SOURCE_VERSION:=$(PKG_REV)

View File

@ -6,6 +6,7 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/haswelliris/h3c.git
PKG_MIRROR_HASH:=3175d4c9d6a4b39a5e4b2ef347e938c092305854057196961f70935723532170
PKG_SOURCE_VERSION:=fdf66c3
PKG_LICENSE:=GPL-3.0

View File

@ -6,6 +6,7 @@ PKG_RELEASE:=2
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/lwz322/k3screenctrl.git
PKG_MIRROR_HASH:=88ca32df297bb7a0e730c2e445909bb9ab37f7144433302e0e7f929c1074b108
PKG_SOURCE_VERSION:=7b81497bb3719d7f3a741307417e39a4505d309d
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_SOURCE_VERSION)
PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.xz