mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-03 04:13:56 +08:00
🌴 Sync 2026-03-05 23:51:42
This commit is contained in:
1 parent
97c049ed0a
commit
0acff03804
14253 files changed
+3893239
No files matched your search
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_VERSION:=1.1.7-r1
|
||||
PKG_RELEASE:=1
|
||||
|
||||
LUCI_TITLE:=LuCI support for shadowrt
|
||||
LUCI_PKGARCH:=all
|
||||
LUCI_DEPENDS:=+lsblk +docker +dockerd +docker-lan-bridge +luci-lib-taskd
|
||||
|
||||
define Package/luci-app-shadowrt/conffiles
|
||||
/etc/config/shadowrt
|
||||
endef
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
|
||||
module("luci.controller.shadowrt", package.seeall)
|
||||
|
||||
function index()
|
||||
entry({"admin", "services", "shadowrt"}, alias("admin", "services", "shadowrt", "overview"), _("ShadoWRT"), 21).dependent = true
|
||||
entry({"admin", "services", "shadowrt", "overview"}, form("shadowrt/overview"), _("Overview"), 10).dependent = true
|
||||
entry({"admin", "services", "shadowrt", "config"}, cbi("shadowrt/config"), _("Install"), 20).dependent = true
|
||||
entry({"admin", "services", "shadowrt", "action"}, post("shadowrt_action"))
|
||||
entry({"admin", "services", "shadowrt", "ls"}, call("shadowrt_ls"))
|
||||
end
|
||||
|
||||
function shadowrt_action()
|
||||
local os = require "os"
|
||||
local rshift = nixio.bit.rshift
|
||||
local action = luci.http.formvalue("action")
|
||||
local name = luci.http.formvalue("name")
|
||||
local r = 1
|
||||
if action == "start" or action == "stop" or action == "restart" or action == "reset_network" or action == "rm" or action == "rmd" or action == "clone" then
|
||||
r = os.execute("/usr/libexec/istorec/shadowrt.sh " .. action .. " " .. luci.util.shellquote(name) .. " >/dev/null 2>&1")
|
||||
r = rshift(r,8)
|
||||
end
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json({ code = r })
|
||||
end
|
||||
|
||||
function shadowrt_ls()
|
||||
local util = require "luci.util"
|
||||
local clones = util.trim(util.exec("/usr/libexec/istorec/shadowrt.sh ls"))
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write("{\n\"code\":200,\n\"clones\":")
|
||||
luci.http.write(clones)
|
||||
luci.http.write("\n}\n")
|
||||
end
|
||||
@@ -0,0 +1,114 @@
|
||||
--[[
|
||||
LuCI - Lua Configuration Interface
|
||||
]]--
|
||||
|
||||
local taskd = require "luci.model.tasks"
|
||||
|
||||
local util = require "luci.util"
|
||||
local jsonc = require "luci.jsonc"
|
||||
|
||||
local block_dir = function()
|
||||
local f = io.popen("lsblk -s -f -b -o NAME,FSSIZE,MOUNTPOINT,FSTYPE --json", "r")
|
||||
local vals = {}
|
||||
if f then
|
||||
local ret = f:read("*all")
|
||||
f:close()
|
||||
local obj = jsonc.parse(ret)
|
||||
for _, val in pairs(obj["blockdevices"]) do
|
||||
local fsize = val["fssize"]
|
||||
if fsize ~= nil and string.len(fsize) > 9 and val["mountpoint"]
|
||||
and val["mountpoint"] ~= "/rom" and val["mountpoint"] ~= "/overlay" and val["mountpoint"] ~= "/ext_overlay" then
|
||||
-- fsize > 1GB and has mountpoint
|
||||
vals[#vals+1] = {val["mountpoint"], val["fstype"]}
|
||||
end
|
||||
end
|
||||
end
|
||||
return vals
|
||||
end
|
||||
|
||||
local homedir = function()
|
||||
local uci = require "luci.model.uci".cursor()
|
||||
local home_dirs = {}
|
||||
home_dirs["main_dir"] = uci:get_first("quickstart", "main", "main_dir", "/root")
|
||||
home_dirs["Configs"] = uci:get_first("quickstart", "main", "conf_dir", home_dirs["main_dir"].."/Configs")
|
||||
return home_dirs
|
||||
end
|
||||
|
||||
local find_paths = function()
|
||||
local blocks = block_dir()
|
||||
local home_dirs = homedir()
|
||||
local path_name = "Configs"
|
||||
local default_path = ''
|
||||
local configs = {}
|
||||
|
||||
default_path = home_dirs[path_name] .. "/ShadoWRT"
|
||||
if #blocks == 0 then
|
||||
table.insert(configs, {default_path, "rootfs"})
|
||||
else
|
||||
for _, val in pairs(blocks) do
|
||||
table.insert(configs, {val[1] .. "/" .. path_name .. "/ShadoWRT", val[2]})
|
||||
end
|
||||
local without_conf_dir = "/root/" .. path_name .. "/ShadoWRT"
|
||||
if default_path == without_conf_dir then
|
||||
default_path = configs[1][1]
|
||||
end
|
||||
end
|
||||
|
||||
return configs, default_path
|
||||
end
|
||||
|
||||
local m, s, o
|
||||
|
||||
m = taskd.docker_map("shadowrt", "shadowrt", "/usr/libexec/istorec/shadowrt.sh",
|
||||
translate("ShadoWRT"),
|
||||
translate("ShadoWRT uses the host's /rom as the base image for its clones, so it only supports hosts using SquashFS firmware. After the host system is upgraded, the clones will also use the new version.") .. "<br>"
|
||||
.. translate("The clone cannot directly access the disk or mount the disk, but /mnt can share the host's.") .. "<br>"
|
||||
.. translate("Some services are unavailable in the clone, such as hardware-related services and OAF. In addition, the Docker service is disabled by default because the host already has the Docker service. If you need to enable the Docker service in the clone, you can enable it under the \"Startup\" menu on the clone's LuCI."))
|
||||
|
||||
s = m:section(TypedSection, "instance", translate("Configuration"),
|
||||
translate("The following network parameters will only take effect during instance fresh installation or after reset:"))
|
||||
s.addremove=false
|
||||
s.anonymous=true
|
||||
|
||||
o = s:option(Value, "id", "ID".."<b>*</b>", translate("This ID will be used as hostname and container name, only letters, numbers, underscore and hyphen are allowed. Same ID will overwrite existing instance, please avoid using the name as other Docker containers"))
|
||||
o.rmempty = false
|
||||
o.datatype = "hostname"
|
||||
|
||||
o = s:option(Value, "data", translate("Data Directory").."<b>*</b>", translate("Will create sub-directory by ID under the selected path, so the path can be shared among multiple instances. Please select a file system with good Linux compatibility, such as ext4, btrfs, zfs, etc."))
|
||||
o.rmempty = false
|
||||
o.datatype = "string"
|
||||
|
||||
o:value("", translate("-- Please choose --"))
|
||||
|
||||
local paths, default_path = find_paths()
|
||||
for _, val in pairs(paths) do
|
||||
o:value(val[1], val[1] .. " (" .. val[2] .. ")")
|
||||
end
|
||||
|
||||
o = s:option(Flag, "mnt", translate("Share /mnt"), translate("Share host's /mnt directory"))
|
||||
o.default = 0
|
||||
|
||||
o = s:option(ListValue, "proto", translate("IP Protocol"), translate("Select how this instance gets its IP address"))
|
||||
o:value("static", translate("Static address"))
|
||||
o:value("dhcp", translate("DHCP client"))
|
||||
o.default = "static"
|
||||
|
||||
o = s:option(Value, "address", translate("IP Address").."<b>*</b>", translate("Format: xxx.xxx.xxx.xxx/xx"))
|
||||
o.rmempty = false
|
||||
o.datatype = "cidr4"
|
||||
o:depends("proto", "static")
|
||||
|
||||
o = s:option(Value, "gateway", translate("Gateway"))
|
||||
o.datatype = "ip4addr(\"nomask\")"
|
||||
o:depends("proto", "static")
|
||||
|
||||
o = s:option(Value, "dns", translate("DNS Servers"), translate("Multiple DNS servers can be separated by spaces, e.g., '8.8.8.8 8.8.4.4'"))
|
||||
o.datatype = "string"
|
||||
o:depends("proto", "static")
|
||||
|
||||
o = s:option(Flag, "dhcp_server", translate("DHCP Server"), translate("Enable DHCP server on this instance"))
|
||||
o.default = 0
|
||||
o.rmempty = false
|
||||
o:depends("proto", "static")
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,14 @@
|
||||
--[[
|
||||
LuCI - Lua Configuration Interface
|
||||
]]--
|
||||
|
||||
local m, s, o
|
||||
|
||||
m = SimpleForm("shadowrt", translate("ShadoWRT"), translate("ShadoWRT can create isolated clones of iStoreOS/OpenWRT."))
|
||||
m.submit = false
|
||||
m.reset = false
|
||||
|
||||
s = m:section(SimpleSection)
|
||||
s.template = "shadowrt/status"
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,351 @@
|
||||
<div class="cbi-section cbi-tblsection">
|
||||
<h3><%:Instance List%></h3>
|
||||
<div class="cbi-section-descr">
|
||||
<details>
|
||||
<summary><%:Instructions%></summary>
|
||||
<ul>
|
||||
<li><%:Executing a shutdown within the clone will restart the clone. To shut down, use the "Stop" button below.%></li>
|
||||
<li><%:Reset network will restore the instance's network configuration to the state at installation.%></li>
|
||||
<li><%:When deleting, you can choose whether to delete the data.%></li>
|
||||
<li><%:"Copy/Edit" will redirect to the "Install" page. If the ID is changed, a new instance will be created; otherwise, the existing instance will be overwritten.%></li>
|
||||
</ul>
|
||||
</details>
|
||||
</div>
|
||||
<table class="table cbi-section-table">
|
||||
<thead>
|
||||
<tr class="tr cbi-section-table-titles anonymous">
|
||||
<th class="th cbi-section-table-cell" ></th>
|
||||
<th class="th cbi-section-table-cell" >ID</th>
|
||||
<th class="th cbi-section-table-cell" ><%:Status%></th>
|
||||
<th class="th cbi-section-table-cell" >IP</th>
|
||||
<th class="th cbi-section-table-cell cbi-section-actions" id="cbi-section-table-head-actions"><em><%:Refreshing…%></em></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="cbi-table-shadowrt-instances">
|
||||
<tr class="tr cbi-section-table-row placeholder"><td class="td"><em><%:Loading view…%></em></td></tr>
|
||||
<% if false then %>
|
||||
<!--
|
||||
<tr class="tr cbi-section-table-row" >
|
||||
<td class="td cbi-value-field">
|
||||
<div>
|
||||
<div class="cbi-checkbox">
|
||||
<input name="selected" type="checkbox" value="istoreos-shadow1">
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="td cbi-value-field" data-title="ID" ><b>istoreos-shadow1</b>
|
||||
</td>
|
||||
<td class="td cbi-value-field" data-title="状态">
|
||||
<b>running</b></td>
|
||||
<td class="td cbi-value-field" data-title="IP">192.168.100.2</td>
|
||||
<td class="td cbi-section-table-cell nowrap cbi-section-actions">
|
||||
<div>
|
||||
<input type="button" class="cbi-button cbi-button-neutral stop" title="停止" value="停止">
|
||||
<input type="button" class="cbi-button cbi-button-neutral start" title="启动" disabled="" value="启动">
|
||||
<input type="button" class="cbi-button cbi-button-neutral reload" title="重启" value="重启">
|
||||
<input type="button" title="复制/编辑" class="btn cbi-button cbi-button-edit" value="复制/编辑">
|
||||
<input type="button" title="删除" class="btn cbi-button cbi-button-remove" value="删除">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
-->
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<style type="text/css">
|
||||
#cbi-table-shadowrt-instances.busy {
|
||||
cursor: progress;
|
||||
}
|
||||
#cbi-table-shadowrt-instances.busy input[type="button"] {
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
cursor: progress;
|
||||
}
|
||||
#cbi-section-table-head-actions>em {
|
||||
display: none;
|
||||
}
|
||||
#cbi-section-table-head-actions.refreshing>em {
|
||||
display: initial;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
<div class="cbi-section">
|
||||
<div class="cbi-section-node">
|
||||
<div id="cbi-section-batch-actions" class="empty">
|
||||
<div style="display: inline-block;">
|
||||
<input class="btn cbi-button cbi-button-neutral" type="button" name="stop" value="<%:Stop%>">
|
||||
</div>
|
||||
<div style="display: inline-block;">
|
||||
<input class="btn cbi-button cbi-button-neutral" type="button" name="start" value="<%:Start%>">
|
||||
</div>
|
||||
<div style="display: inline-block;">
|
||||
<input class="btn cbi-button cbi-button-neutral" type="button" name="restart" value="<%:Restart%>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style type="text/css">
|
||||
#cbi-section-batch-actions.busy, #cbi-section-batch-actions.busy input {
|
||||
cursor: progress;
|
||||
}
|
||||
#cbi-section-batch-actions.busy input, #cbi-section-batch-actions.empty input {
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
}
|
||||
</style>
|
||||
|
||||
<%
|
||||
local util = require "luci.util"
|
||||
local containers = util.trim(util.exec("/usr/libexec/istorec/shadowrt.sh ls"))
|
||||
-%>
|
||||
<script type="text/javascript">
|
||||
window.addEventListener("load", function() {
|
||||
var containers = <%=containers%>;
|
||||
console.log(containers);
|
||||
var table = document.getElementById("cbi-table-shadowrt-instances");
|
||||
var tableHeadActions = document.getElementById("cbi-section-table-head-actions");
|
||||
var batchActionsView = document.getElementById("cbi-section-batch-actions");
|
||||
|
||||
const State = {
|
||||
IDLE: 0,
|
||||
BUSY: 1,
|
||||
REFRESH: 2
|
||||
};
|
||||
var state = State.IDLE;
|
||||
|
||||
var busy = function() {
|
||||
table.classList.add("busy");
|
||||
tableHeadActions.classList.add("spinning");
|
||||
batchActionsView.classList.add("busy");
|
||||
};
|
||||
var idle = function() {
|
||||
table.classList.remove("busy");
|
||||
tableHeadActions.classList.remove("spinning");
|
||||
batchActionsView.classList.remove("busy");
|
||||
};
|
||||
var render = undefined;
|
||||
var refreshTimer = undefined;
|
||||
var refresh = function(inbusy) {
|
||||
if (state !== State.IDLE) {
|
||||
return;
|
||||
}
|
||||
state = State.REFRESH;
|
||||
if (!inbusy)
|
||||
busy();
|
||||
tableHeadActions.classList.add("refreshing");
|
||||
(new XHR()).get('<%=url([[admin/services/shadowrt/ls]])%>', {},
|
||||
function(xhr) {
|
||||
if (state !== State.REFRESH) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var resp = JSON.parse(xhr.responseText);
|
||||
if (resp.code === 200) {
|
||||
containers = resp.clones;
|
||||
render();
|
||||
} else {
|
||||
console.error("Failed to get instance list.");
|
||||
}
|
||||
} finally {
|
||||
idle();
|
||||
tableHeadActions.classList.remove("refreshing");
|
||||
refreshTimer = setTimeout(refresh, 5000);
|
||||
state = State.IDLE;
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
var doAction = function(action, name, cb) {
|
||||
if (state !== State.IDLE) {
|
||||
return;
|
||||
}
|
||||
if (refreshTimer !== undefined) {
|
||||
clearTimeout(refreshTimer);
|
||||
refreshTimer = undefined;
|
||||
}
|
||||
state = State.BUSY;
|
||||
busy();
|
||||
(new XHR()).post('<%=url([[admin/services/shadowrt/action]])%>', { token: '<%=token%>', action: action, name: name },
|
||||
function() {
|
||||
state = State.IDLE;
|
||||
if (cb) {
|
||||
try {
|
||||
cb();
|
||||
} finally {
|
||||
idle();
|
||||
}
|
||||
} else {
|
||||
refresh(true);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
var action_stop = function(name) {
|
||||
console.log("Stop ", name);
|
||||
doAction("stop", name);
|
||||
};
|
||||
var action_start = function(name) {
|
||||
console.log("Start ", name);
|
||||
doAction("start", name);
|
||||
};
|
||||
var action_restart = function(name) {
|
||||
console.log("Restart ", name);
|
||||
doAction("restart", name);
|
||||
};
|
||||
var action_edit = function(name) {
|
||||
console.log("Edit ", name);
|
||||
doAction("clone", name, function() {
|
||||
refreshTimer = setTimeout(refresh, 1000);
|
||||
location.href = '<%=url([[admin/services/shadowrt/config]])%>';
|
||||
});
|
||||
};
|
||||
var action_reset_network = function(name) {
|
||||
console.log("Reset Network ", name);
|
||||
if (!confirm("<%:Are you sure you want to reset the network configuration of this instance?%>"))
|
||||
return;
|
||||
doAction("reset_network", name);
|
||||
};
|
||||
var action_remove = function(name) {
|
||||
console.log("Remove ", name);
|
||||
if (!confirm("<%:Are you sure you want to delete this instance?%>"))
|
||||
return;
|
||||
var deleteData = confirm("<%:Delete instance data as well? (If retained, future instances with the same ID in the same data directory will use this data)%>");
|
||||
doAction(deleteData?"rmd":"rm", name);
|
||||
};
|
||||
var selected = [];
|
||||
var on_selected = function(name, checked) {
|
||||
if (checked) {
|
||||
selected.push(name);
|
||||
} else {
|
||||
var index = selected.indexOf(name);
|
||||
if (index !== -1)
|
||||
selected.splice(index, 1);
|
||||
}
|
||||
if (selected.length === 1) {
|
||||
batchActionsView.classList.remove("empty");
|
||||
} else if (selected.length === 0) {
|
||||
batchActionsView.classList.add("empty");
|
||||
}
|
||||
};
|
||||
var reset_selected = function(newSelected) {
|
||||
selected = newSelected || [];
|
||||
if (selected.length > 0) {
|
||||
batchActionsView.classList.remove("empty");
|
||||
} else {
|
||||
batchActionsView.classList.add("empty");
|
||||
}
|
||||
};
|
||||
var batchActions = {
|
||||
stop: action_stop,
|
||||
start: action_start,
|
||||
restart: action_restart
|
||||
};
|
||||
batchActionsView.querySelectorAll('input[type="button"]').forEach(function(button) {
|
||||
var action = button.name;
|
||||
if (batchActions[action]) {
|
||||
var fn = batchActions[action];
|
||||
button.addEventListener("click", function() {
|
||||
fn(selected.join(" "));
|
||||
});
|
||||
}
|
||||
});
|
||||
var status_map = {
|
||||
"running": "<b><%:Running%></b>",
|
||||
"starting": "<i><%:Starting%></i>",
|
||||
"restarting": "<i><%:Restarting%></i>",
|
||||
"exited": "<%:Stopped%>"
|
||||
};
|
||||
var actions = [
|
||||
{fn: action_stop, title: "<%:Stop%>", class: "cbi-button cbi-button-neutral", disabled: function(status) { return status === "created" || status === "exited"; }},
|
||||
{fn: action_start, title: "<%:Start%>", class: "cbi-button cbi-button-neutral", disabled: function(status) { return status === "running" || status === "starting"; }},
|
||||
{fn: action_restart, title: "<%:Restart%>", class: "cbi-button cbi-button-neutral", disabled: function(status) { return false; }},
|
||||
{fn: action_edit, title: "<%:Copy/Edit%>", class: "cbi-button cbi-button-edit", disabled: function(status) { return false; }},
|
||||
{fn: action_reset_network, title: "<%:Reset Network%>", class: "cbi-button cbi-button-remove", disabled: function(status) { return false; }},
|
||||
{fn: action_remove, title: "<%:Delete%>", class: "cbi-button cbi-button-remove", disabled: function(status) { return false; }},
|
||||
];
|
||||
render = function() {
|
||||
var newSelected = [];
|
||||
var rows = containers.map(function(container) {
|
||||
var name = container.name;
|
||||
var status = container.status;
|
||||
var ip = container.ip;
|
||||
var row = document.createElement("tr");
|
||||
row.className = "tr cbi-section-table-row";
|
||||
|
||||
var selectCell = document.createElement("td");
|
||||
selectCell.className = "td cbi-value-field";
|
||||
var selectDiv = document.createElement("div");
|
||||
var checkboxDiv = document.createElement("div");
|
||||
checkboxDiv.className = "cbi-checkbox";
|
||||
var checkboxInput = document.createElement("input");
|
||||
checkboxInput.type = "checkbox";
|
||||
checkboxInput.name = "selected";
|
||||
checkboxInput.value = name;
|
||||
checkboxInput.checked = selected.indexOf(name) !== -1;
|
||||
if (checkboxInput.checked)
|
||||
newSelected.push(name);
|
||||
checkboxInput.addEventListener('change', function(e){on_selected(e.target.value, e.target.checked)});
|
||||
checkboxDiv.appendChild(checkboxInput);
|
||||
selectDiv.appendChild(checkboxDiv);
|
||||
selectCell.appendChild(selectDiv);
|
||||
row.appendChild(selectCell);
|
||||
|
||||
var idCell = document.createElement("td");
|
||||
idCell.className = "td cbi-value-field";
|
||||
idCell.setAttribute("data-title", "ID");
|
||||
idCell.innerHTML = "<b>" + name + "</b>";
|
||||
row.appendChild(idCell);
|
||||
|
||||
var statusCell = document.createElement("td");
|
||||
statusCell.className = "td cbi-value-field";
|
||||
statusCell.setAttribute("data-title", "<%:Status%>");
|
||||
statusCell.innerHTML = status_map[status] || status;
|
||||
row.appendChild(statusCell);
|
||||
|
||||
var ipCell = document.createElement("td");
|
||||
ipCell.className = "td cbi-value-field";
|
||||
ipCell.setAttribute("data-title", "IP");
|
||||
ipCell.innerHTML = '<a href="http://' + ip + '" target="_blank">' + ip + '</a>';
|
||||
row.appendChild(ipCell);
|
||||
|
||||
var actionCell = document.createElement("td");
|
||||
actionCell.className = "td cbi-section-table-cell nowrap cbi-section-actions";
|
||||
var actionDiv = document.createElement("div");
|
||||
|
||||
var actionBtns = actions.map(function(action) {
|
||||
var input = document.createElement("input");
|
||||
input.type = "button";
|
||||
input.className = action.class;
|
||||
input.title = action.title;
|
||||
input.value = action.title;
|
||||
if (action.disabled(status)) {
|
||||
input.disabled = true;
|
||||
}
|
||||
input.addEventListener("click", action.fn.bind(null, name));
|
||||
return input;
|
||||
});
|
||||
actionDiv.replaceChildren(...actionBtns);
|
||||
actionCell.appendChild(actionDiv);
|
||||
row.appendChild(actionCell);
|
||||
return row;
|
||||
});
|
||||
|
||||
if (rows.length === 0) {
|
||||
var row = document.createElement("tr");
|
||||
row.className = "tr cbi-section-table-row placeholder";
|
||||
var cell = document.createElement("td");
|
||||
cell.className = "td";
|
||||
cell.innerHTML = '<em><%=translatef([[No instances yet, go to <a href="%s">Install</a>.]], url([[admin/services/shadowrt/config]]))%></em>';
|
||||
row.appendChild(cell);
|
||||
rows.push(row);
|
||||
}
|
||||
reset_selected(newSelected);
|
||||
|
||||
table.replaceChildren(...rows);
|
||||
};
|
||||
render();
|
||||
refreshTimer = setTimeout(refresh, 10000);
|
||||
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
@@ -0,0 +1,228 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:10
|
||||
msgid ""
|
||||
"\"Copy/Edit\" will redirect to the \"Install\" page. If the ID is changed, a "
|
||||
"new instance will be created; otherwise, the existing instance will be "
|
||||
"overwritten."
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:81
|
||||
msgid "-- Please choose --"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:210
|
||||
msgid "Are you sure you want to delete this instance?"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:204
|
||||
msgid ""
|
||||
"Are you sure you want to reset the network configuration of this instance?"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:68
|
||||
msgid "Configuration"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:262
|
||||
msgid "Copy/Edit"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:109
|
||||
msgid "DHCP Server"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:93
|
||||
msgid "DHCP client"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:105
|
||||
msgid "DNS Servers"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:77
|
||||
msgid "Data Directory"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:264
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:212
|
||||
msgid ""
|
||||
"Delete instance data as well? (If retained, future instances with the same "
|
||||
"ID in the same data directory will use this data)"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:109
|
||||
msgid "Enable DHCP server on this instance"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:7
|
||||
msgid ""
|
||||
"Executing a shutdown within the clone will restart the clone. To shut down, "
|
||||
"use the \"Stop\" button below."
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:96
|
||||
msgid "Format: xxx.xxx.xxx.xxx/xx"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:101
|
||||
msgid "Gateway"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:96
|
||||
msgid "IP Address"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:91
|
||||
msgid "IP Protocol"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/controller/shadowrt.lua:7
|
||||
msgid "Install"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:2
|
||||
msgid "Instance List"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:5
|
||||
msgid "Instructions"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:25
|
||||
msgid "Loading view…"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:105
|
||||
msgid ""
|
||||
"Multiple DNS servers can be separated by spaces, e.g., '8.8.8.8 8.8.4.4'"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:338
|
||||
msgid "No instances yet, go to <a href=\"%s\">Install</a>."
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/controller/shadowrt.lua:6
|
||||
msgid "Overview"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:21
|
||||
msgid "Refreshing…"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:263
|
||||
msgid "Reset Network"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:8
|
||||
msgid ""
|
||||
"Reset network will restore the instance's network configuration to the state "
|
||||
"at installation."
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:82 luasrc/view/shadowrt/status.htm:261
|
||||
msgid "Restart"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:255
|
||||
msgid "Restarting"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:253
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:91
|
||||
msgid "Select how this instance gets its IP address"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/controller/shadowrt.lua:5 luasrc/model/cbi/shadowrt/overview.lua:7
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:63
|
||||
msgid "ShadoWRT"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/overview.lua:7
|
||||
msgid "ShadoWRT can create isolated clones of iStoreOS/OpenWRT."
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:64
|
||||
msgid ""
|
||||
"ShadoWRT uses the host's /rom as the base image for its clones, so it only "
|
||||
"supports hosts using SquashFS firmware. After the host system is upgraded, "
|
||||
"the clones will also use the new version."
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:88
|
||||
msgid "Share /mnt"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:88
|
||||
msgid "Share host's /mnt directory"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:66
|
||||
msgid ""
|
||||
"Some services are unavailable in the clone, such as hardware-related "
|
||||
"services and OAF. In addition, the Docker service is disabled by default "
|
||||
"because the host already has the Docker service. If you need to enable the "
|
||||
"Docker service in the clone, you can enable it under the \"Startup\" menu on "
|
||||
"the clone's LuCI."
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:79 luasrc/view/shadowrt/status.htm:260
|
||||
msgid "Start"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:254
|
||||
msgid "Starting"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:92
|
||||
msgid "Static address"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:19 luasrc/view/shadowrt/status.htm:301
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:76 luasrc/view/shadowrt/status.htm:259
|
||||
msgid "Stop"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:256
|
||||
msgid "Stopped"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:65
|
||||
msgid ""
|
||||
"The clone cannot directly access the disk or mount the disk, but /mnt can "
|
||||
"share the host's."
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:69
|
||||
msgid ""
|
||||
"The following network parameters will only take effect during instance fresh "
|
||||
"installation or after reset:"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:73
|
||||
msgid ""
|
||||
"This ID will be used as hostname and container name, only letters, numbers, "
|
||||
"underscore and hyphen are allowed. Same ID will overwrite existing instance, "
|
||||
"please avoid using the name as other Docker containers"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:9
|
||||
msgid "When deleting, you can choose whether to delete the data."
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:77
|
||||
msgid ""
|
||||
"Will create sub-directory by ID under the selected path, so the path can be "
|
||||
"shared among multiple instances. Please select a file system with good Linux "
|
||||
"compatibility, such as ext4, btrfs, zfs, etc."
|
||||
msgstr ""
|
||||
@@ -0,0 +1,242 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language: zh_Hans\n"
|
||||
"Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:10
|
||||
msgid ""
|
||||
"\"Copy/Edit\" will redirect to the \"Install\" page. If the ID is changed, a "
|
||||
"new instance will be created; otherwise, the existing instance will be "
|
||||
"overwritten."
|
||||
msgstr ""
|
||||
"“复制/编辑”将跳转到“安装”页面,如果修改ID则创建新实例,否则覆盖原有实例。"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:81
|
||||
msgid "-- Please choose --"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:210
|
||||
msgid "Are you sure you want to delete this instance?"
|
||||
msgstr "确定要删除此实例吗?"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:204
|
||||
msgid ""
|
||||
"Are you sure you want to reset the network configuration of this instance?"
|
||||
msgstr "确定要重置此实例的网络配置吗?"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:68
|
||||
msgid "Configuration"
|
||||
msgstr "配置"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:262
|
||||
msgid "Copy/Edit"
|
||||
msgstr "复制/编辑"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:109
|
||||
msgid "DHCP Server"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:93
|
||||
msgid "DHCP client"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:105
|
||||
msgid "DNS Servers"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:77
|
||||
msgid "Data Directory"
|
||||
msgstr "数据文件夹"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:264
|
||||
msgid "Delete"
|
||||
msgstr "删除"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:212
|
||||
msgid ""
|
||||
"Delete instance data as well? (If retained, future instances with the same "
|
||||
"ID in the same data directory will use this data)"
|
||||
msgstr ""
|
||||
"是否同时删除实例数据?(若保留,未来相同数据目录下创建相同 ID 的实例将会使用"
|
||||
"此数据)"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:109
|
||||
msgid "Enable DHCP server on this instance"
|
||||
msgstr "这个实例提供 DHCP 服务"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:7
|
||||
msgid ""
|
||||
"Executing a shutdown within the clone will restart the clone. To shut down, "
|
||||
"use the \"Stop\" button below."
|
||||
msgstr "分身内执行关机会重启分身,如果要关机,使用下面的“停止”按钮。"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:96
|
||||
msgid "Format: xxx.xxx.xxx.xxx/xx"
|
||||
msgstr "格式:xxx.xxx.xxx.xxx/xx"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:101
|
||||
msgid "Gateway"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:96
|
||||
msgid "IP Address"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:91
|
||||
msgid "IP Protocol"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/controller/shadowrt.lua:7
|
||||
msgid "Install"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:2
|
||||
msgid "Instance List"
|
||||
msgstr "实例列表"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:5
|
||||
msgid "Instructions"
|
||||
msgstr "操作说明"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:25
|
||||
msgid "Loading view…"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:105
|
||||
msgid ""
|
||||
"Multiple DNS servers can be separated by spaces, e.g., '8.8.8.8 8.8.4.4'"
|
||||
msgstr "多个 DNS 可以用空格隔开,例如‘8.8.8.8 8.8.4.4’"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:338
|
||||
msgid "No instances yet, go to <a href=\"%s\">Install</a>."
|
||||
msgstr "还没有实例,前往<a href=\"%s\">安装</a>"
|
||||
|
||||
#: luasrc/controller/shadowrt.lua:6
|
||||
msgid "Overview"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:21
|
||||
msgid "Refreshing…"
|
||||
msgstr "刷新中…"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:263
|
||||
msgid "Reset Network"
|
||||
msgstr "重置网络"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:8
|
||||
msgid ""
|
||||
"Reset network will restore the instance's network configuration to the state "
|
||||
"at installation."
|
||||
msgstr "重置网络可将实例的网络配置重置为安装时的状态。"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:82 luasrc/view/shadowrt/status.htm:261
|
||||
msgid "Restart"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:255
|
||||
msgid "Restarting"
|
||||
msgstr "重启中"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:253
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:91
|
||||
msgid "Select how this instance gets its IP address"
|
||||
msgstr "实例如何获取自身 IP 地址"
|
||||
|
||||
#: luasrc/controller/shadowrt.lua:5 luasrc/model/cbi/shadowrt/overview.lua:7
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:63
|
||||
msgid "ShadoWRT"
|
||||
msgstr "ShadoWRT"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/overview.lua:7
|
||||
msgid "ShadoWRT can create isolated clones of iStoreOS/OpenWRT."
|
||||
msgstr "ShadoWRT 可创建隔离的 iStoreOS/OpenWRT 的分身"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:64
|
||||
msgid ""
|
||||
"ShadoWRT uses the host's /rom as the base image for its clones, so it only "
|
||||
"supports hosts using SquashFS firmware. After the host system is upgraded, "
|
||||
"the clones will also use the new version."
|
||||
msgstr ""
|
||||
"ShadoWRT 使用宿主的 /rom 作为分身的基础镜像,所以只支持使用 SquashFS 固件的宿"
|
||||
"主。宿主系统升级后,分身也会使用新版本。"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:88
|
||||
msgid "Share /mnt"
|
||||
msgstr "共享 /mnt"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:88
|
||||
msgid "Share host's /mnt directory"
|
||||
msgstr "共享宿主的 /mnt 文件夹"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:66
|
||||
msgid ""
|
||||
"Some services are unavailable in the clone, such as hardware-related "
|
||||
"services and OAF. In addition, the Docker service is disabled by default "
|
||||
"because the host already has the Docker service. If you need to enable the "
|
||||
"Docker service in the clone, you can enable it under the \"Startup\" menu on "
|
||||
"the clone's LuCI."
|
||||
msgstr ""
|
||||
"分身中部分服务无法使用,例如硬件有关的服务、应用过滤等;另外,Docker 服务默认"
|
||||
"禁用,因为宿主已经有 Docker 服务,如需在分身中启用 Docker 服务,可在分身的后"
|
||||
"台页面“启动项”中开启。"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:79 luasrc/view/shadowrt/status.htm:260
|
||||
msgid "Start"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:254
|
||||
msgid "Starting"
|
||||
msgstr "启动中"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:92
|
||||
msgid "Static address"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:19 luasrc/view/shadowrt/status.htm:301
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:76 luasrc/view/shadowrt/status.htm:259
|
||||
msgid "Stop"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:256
|
||||
msgid "Stopped"
|
||||
msgstr ""
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:65
|
||||
msgid ""
|
||||
"The clone cannot directly access the disk or mount the disk, but /mnt can "
|
||||
"share the host's."
|
||||
msgstr "分身无法直接访问磁盘,也无法挂载磁盘,不过 /mnt 可以共享宿主的。"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:69
|
||||
msgid ""
|
||||
"The following network parameters will only take effect during instance fresh "
|
||||
"installation or after reset:"
|
||||
msgstr "以下网络参数只在实例全新安装或重置以后才生效:"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:73
|
||||
msgid ""
|
||||
"This ID will be used as hostname and container name, only letters, numbers, "
|
||||
"underscore and hyphen are allowed. Same ID will overwrite existing instance, "
|
||||
"please avoid using the name as other Docker containers"
|
||||
msgstr ""
|
||||
"ID 将作为实例的主机名和容器名,只允许字母数字下划线和减号。相同 ID 将覆盖已有"
|
||||
"实例,请避免使用与其他 Docker 容器相同的名称"
|
||||
|
||||
#: luasrc/view/shadowrt/status.htm:9
|
||||
msgid "When deleting, you can choose whether to delete the data."
|
||||
msgstr "“删除”时可选择是否删除数据。"
|
||||
|
||||
#: luasrc/model/cbi/shadowrt/config.lua:77
|
||||
msgid ""
|
||||
"Will create sub-directory by ID under the selected path, so the path can be "
|
||||
"shared among multiple instances. Please select a file system with good Linux "
|
||||
"compatibility, such as ext4, btrfs, zfs, etc."
|
||||
msgstr ""
|
||||
"会在选择的路径下创建以 ID 命名的子文件夹,所以此路径可以被多个实例共用。请选"
|
||||
"择 Linux 兼容性好的文件系统,例如 ext4,btrfs,zfs 等"
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
zh-cn
|
||||
@@ -0,0 +1,12 @@
|
||||
config main 'main'
|
||||
|
||||
config instance
|
||||
option 'id' 'shadowrt0'
|
||||
# option 'data' '/mnt/data/shadowrt'
|
||||
option 'mnt' '0'
|
||||
option 'proto' 'static'
|
||||
option 'address' '192.168.100.2/24'
|
||||
option 'gateway' '192.168.100.1'
|
||||
option 'dns' '192.168.100.1'
|
||||
option 'dhcp_server' '0'
|
||||
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
#!/bin/sh
|
||||
# Author jjm2473@gmail.com
|
||||
|
||||
ACTION="${1}"
|
||||
shift 1
|
||||
|
||||
do_install() {
|
||||
local id="`uci get shadowrt.@instance[0].id 2>/dev/null`"
|
||||
local data="`uci get shadowrt.@instance[0].data 2>/dev/null`"
|
||||
local mnt="`uci get shadowrt.@instance[0].mnt 2>/dev/null`"
|
||||
local proto=`uci get shadowrt.@instance[0].proto 2>/dev/null`
|
||||
local address=`uci get shadowrt.@instance[0].address 2>/dev/null`
|
||||
local gateway=`uci get shadowrt.@instance[0].gateway 2>/dev/null`
|
||||
local dns="`uci get shadowrt.@instance[0].dns 2>/dev/null`"
|
||||
local dhcp_server=`uci get shadowrt.@instance[0].dhcp_server 2>/dev/null`
|
||||
local ports="`uci get shadowrt.@instance[0].ports 2>/dev/null`"
|
||||
|
||||
if [ -z "$data" ]; then
|
||||
echo "data path is empty!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[ -s /rom/etc/openwrt_release ] || {
|
||||
echo "/rom is not a openwrt rootfs!" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ "$proto" = "static" ]; then
|
||||
if [ -z "$address" ]; then
|
||||
echo "static ip requires address!" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
/etc/init.d/docker-lan start || {
|
||||
echo "create docker-lan bridge failed!" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
local alpine_image="alpine:3.22.2"
|
||||
if ! docker image inspect -f '{}' "$alpine_image" >/dev/null 2>&1; then
|
||||
echo "pulling alpine image $alpine_image ..."
|
||||
docker pull "$alpine_image" || exit 1
|
||||
fi
|
||||
|
||||
if [ -d "$data/$id" ]; then
|
||||
echo "WARNING: $data/$id already exists, may use old data." >&2
|
||||
fi
|
||||
|
||||
local config="{\"id\":\"$id\",\"data\":\"$data\",\"mnt\":\"$mnt\",\"proto\":\"$proto\",\"address\":\"$address\",\"gateway\":\"$gateway\",\"dns\":\"$dns\",\"dhcp_server\":\"$dhcp_server\",\"ports\":\"$ports\"}"
|
||||
|
||||
local cmd="docker run --restart=unless-stopped -d \
|
||||
--stop-signal SIGINT \
|
||||
--stop-timeout 30 \
|
||||
--security-opt seccomp=unconfined \
|
||||
--security-opt apparmor=unconfined \
|
||||
--cap-add=SYS_ADMIN \
|
||||
--cap-add=SYS_CHROOT \
|
||||
--cap-drop=MKNOD \
|
||||
--cap-add=LEASE \
|
||||
--cap-add=SETGID \
|
||||
--cap-add=SETUID \
|
||||
--cap-add=NET_ADMIN \
|
||||
--cap-add=NET_RAW \
|
||||
--cap-add=NET_BIND_SERVICE \
|
||||
--network docker-lan \
|
||||
-v /usr/share/shadowrt/container:/shadowrt:ro \
|
||||
--entrypoint /shadowrt/entrypoint.sh \
|
||||
-v /dev/net:/dev/net \
|
||||
--device /dev/fuse:/dev/fuse \
|
||||
-v /rom:/rom:ro \
|
||||
--label creator=shadowrt \
|
||||
--name '$id' \
|
||||
--hostname '$id' \
|
||||
--label 'com.shadowrt.config=$config' \
|
||||
-v '$data/$id/overlay:/overlay:rw' "
|
||||
|
||||
[ -n "$proto" ] && cmd="$cmd -e IP_PROTO=$proto"
|
||||
if [ "$proto" = "static" ]; then
|
||||
[ -n "$address" ] && cmd="$cmd -e IP_ADDRESS=$address"
|
||||
[ -n "$gateway" ] && cmd="$cmd -e IP_GATEWAY=$gateway"
|
||||
[ -n "$dns" ] && cmd="$cmd -e 'IP_DNS=$dns'"
|
||||
fi
|
||||
[ "$dhcp_server" = "1" -o "$dhcp_server" = "on" ] && cmd="$cmd -e DHCP_SERVER=on"
|
||||
if [ -n "$ports" ]; then
|
||||
for p in $ports; do
|
||||
cmd="$cmd -p $p:$p"
|
||||
done
|
||||
fi
|
||||
if [ -n "$dns" ]; then
|
||||
for d in $dns; do
|
||||
cmd="$cmd --dns $d"
|
||||
done
|
||||
fi
|
||||
|
||||
local tz="`uci get system.@system[0].zonename | sed 's/ /_/g'`"
|
||||
[ -z "$tz" ] || cmd="$cmd -e TZ=$tz"
|
||||
|
||||
if [ "$mnt" = "1" -o "$mnt" = "on" ]; then
|
||||
cmd="$cmd -v /mnt:/mnt"
|
||||
mountpoint -q /mnt && cmd="$cmd:rshared"
|
||||
fi
|
||||
cmd="$cmd $alpine_image"
|
||||
|
||||
echo "stopping existing container..."
|
||||
docker stop "$id" >/dev/null 2>&1
|
||||
docker rm -f "$id"
|
||||
|
||||
echo "starting shadowrt instance $id..."
|
||||
echo "$cmd"
|
||||
eval "$cmd"
|
||||
|
||||
}
|
||||
|
||||
do_ls() {
|
||||
local name state ip
|
||||
|
||||
echo "["
|
||||
docker ps -a -f 'label=creator=shadowrt' --format '{{.Names}} {{.State}}' | sort -n | while read name state; do
|
||||
ip=
|
||||
if [ "$state" = "running" ]; then
|
||||
ip=`docker exec "$name" ip addr show dev br-lan | grep -m1 'inet ' | head -1 | sed -nE 's#.*inet ([0-9\.]*)/([0-9]*) .*#\1#p'`
|
||||
if [ -z "$ip" ]; then
|
||||
docker exec "$name" test -e /etc/openwrt_release -a ! -e /rom/note || state="starting"
|
||||
fi
|
||||
fi
|
||||
echo '{"name":"'"$name"'","status":"'"$state"'","ip":"'"$ip"'"},'
|
||||
#docker container inspect -f '{"name":"'"$name"'","status":"'"$state"'","config":{{index .Config.Labels "com.shadowrt.config"}},"ip":"'"$ip"'"},' "$name"
|
||||
done | head -c -2
|
||||
echo ""
|
||||
echo "]"
|
||||
}
|
||||
|
||||
do_clone() {
|
||||
local config_json="`docker container inspect -f '{{index .Config.Labels "com.shadowrt.config"}}' "$1"`"
|
||||
[ -z "$config_json" ] && {
|
||||
echo "container $1 not found!" >&2
|
||||
return 1
|
||||
}
|
||||
{
|
||||
echo "delete shadowrt.@instance[0]"
|
||||
echo "add shadowrt instance"
|
||||
echo "$config_json" | jsonfilter -e 'id=$.id' \
|
||||
-e 'data=$.data' \
|
||||
-e 'mnt=$.mnt' \
|
||||
-e 'proto=$.proto' \
|
||||
-e 'address=$.address' \
|
||||
-e 'gateway=$.gateway' \
|
||||
-e 'dns=$.dns' \
|
||||
-e 'dhcp_server=$.dhcp_server' \
|
||||
-e 'ports=$.ports' | sed -e 's/; /\n/g' | sed -e 's/^export /set shadowrt.@instance[0]./g'
|
||||
echo "commit shadowrt"
|
||||
} | uci batch
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
do_reset_network() {
|
||||
local name="$1"
|
||||
local running=false
|
||||
local shell
|
||||
|
||||
docker exec "$name" test -e /etc/openwrt_release -a ! -e /rom/note >/dev/null 2>&1 && running=true
|
||||
if $running; then
|
||||
shell="exec docker exec -i -w / '$name' /bin/sh"
|
||||
else
|
||||
docker stop "$name" >/dev/null 2>&1
|
||||
local data="`docker container inspect -f '{{index .Config.Labels "com.shadowrt.config"}}' "$name" | jsonfilter -e '$.data'`"
|
||||
local dir="$data/$name/overlay/upper"
|
||||
[ -d "$dir" ] || return 0
|
||||
shell="cd '$dir' && exec /bin/sh"
|
||||
fi
|
||||
{
|
||||
cat <<-EOF
|
||||
for f in etc/config/network etc/board.json; do
|
||||
rm -f "\$f"
|
||||
done
|
||||
EOF
|
||||
if $running; then
|
||||
cat <<-EOF
|
||||
/bin/board_detect
|
||||
/bin/config_generate
|
||||
/bin/sh -c ". /rom/etc/uci-defaults/zzz-dockerenv"
|
||||
/bin/sh -c '. /rom/etc/uci-defaults/12_network-generate-ula'
|
||||
/etc/init.d/network restart
|
||||
sleep 2
|
||||
EOF
|
||||
else
|
||||
echo 'rm -f etc/uci-defaults/zzz-dockerenv etc/uci-defaults/12_network-generate-ula'
|
||||
fi
|
||||
} | sh -c "$shell"
|
||||
}
|
||||
|
||||
check_all_ready() {
|
||||
local names="$1"
|
||||
local name
|
||||
local ret=0
|
||||
for name in $names; do
|
||||
if ! docker exec "$name" test -e /etc/openwrt_release -a ! -e /rom/note; then
|
||||
ret=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
return $ret
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "usage: $0 sub-command"
|
||||
echo "where sub-command is one of:"
|
||||
echo " install Install/Replace a instance"
|
||||
echo " ls List all instances"
|
||||
echo " rm/start/stop/restart {ID} Remove/Start/Stop/Restart the instance"
|
||||
echo " clone {ID} Clone an existing instance to uci"
|
||||
echo " rmd {ID} Remove an existing instance and its data"
|
||||
echo " reset_network {ID} Reset network configuration inside the instance"
|
||||
echo " status Dummy status for taskd"
|
||||
|
||||
}
|
||||
|
||||
case "${ACTION}" in
|
||||
"install")
|
||||
do_install
|
||||
;;
|
||||
"rm" | "rmd")
|
||||
if [ -n "$1" ]; then
|
||||
docker stop "$1" >/dev/null 2>&1
|
||||
if [ "$ACTION" = "rmd" ]; then
|
||||
data="`docker container inspect -f '{{index .Config.Labels "com.shadowrt.config"}}' "$1" | jsonfilter -e '$.data'`"
|
||||
[ -n "$data" ] && rm -rf "$data/$1"
|
||||
fi
|
||||
docker rm -f "$1"
|
||||
fi
|
||||
;;
|
||||
"reset_network")
|
||||
if [ -n "$1" ]; then
|
||||
do_reset_network "$1"
|
||||
fi
|
||||
;;
|
||||
"start" | "stop" | "restart")
|
||||
if [ -n "$1" ]; then
|
||||
docker "${ACTION}" $1
|
||||
if [ "$ACTION" = "start" -o "$ACTION" = "restart" ]; then
|
||||
sleep 2
|
||||
for i in $(seq 1 5); do
|
||||
check_all_ready "$1" && break
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
"clone")
|
||||
if [ -n "$1" ]; then
|
||||
do_clone "$1" || exit 1
|
||||
fi
|
||||
;;
|
||||
"status")
|
||||
# hack, return empty string, so lib-taskd thinks container is not installed
|
||||
exit 0
|
||||
;;
|
||||
"ls")
|
||||
do_ls
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"luci-app-shadowrt": {
|
||||
"description": "Grant UCI access for luci-app-shadowrt",
|
||||
"read": {
|
||||
"uci": [ "shadowrt" ]
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "shadowrt" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
|
||||
#docker run -d --rm \
|
||||
exec docker run -it --rm \
|
||||
--stop-signal SIGINT \
|
||||
--stop-timeout 30 \
|
||||
--security-opt seccomp=unconfined \
|
||||
--security-opt apparmor=unconfined \
|
||||
--cap-add=SYS_ADMIN \
|
||||
--cap-add=SYS_CHROOT \
|
||||
--cap-drop=MKNOD \
|
||||
--cap-add=LEASE \
|
||||
--cap-add=SETGID \
|
||||
--cap-add=SETUID \
|
||||
--cap-add=NET_ADMIN \
|
||||
--cap-add=NET_RAW \
|
||||
--cap-add=NET_BIND_SERVICE \
|
||||
-v /usr/share/shadowrt/container:/shadowrt:ro \
|
||||
--entrypoint /shadowrt/entrypoint.sh \
|
||||
--name istoreos-shadow0 \
|
||||
--hostname istoreos-shadow0 \
|
||||
--label creator=istoreos-shadow \
|
||||
--label com.istoreos.shadow.id=istoreos-shadow0 \
|
||||
--dns 172.17.0.1 \
|
||||
--dns 8.8.8.8 \
|
||||
-p 8080:80 \
|
||||
-p 17681:7681 \
|
||||
-v /dev/net:/dev/net \
|
||||
--device /dev/fuse:/dev/fuse \
|
||||
-v /rom:/rom:ro \
|
||||
-v /mnt:/mnt:rshared \
|
||||
-v /mnt/data/is-diff/0:/overlay:rw \
|
||||
alpine
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
|
||||
# env
|
||||
# IP_PROTO=dhcp|static
|
||||
# IP_ADDRESS=ipv4/prefixlen
|
||||
# IP_GATEWAY=ipv4
|
||||
# IP_DNS="dns1 dns2 ..."
|
||||
# DHCP_SERVER=on|off
|
||||
|
||||
[ -s /rom/etc/openwrt_release ] || {
|
||||
echo "/rom is not a openwrt rootfs!" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -e /rom/.dockerenv ] && {
|
||||
echo "/rom is already a docker container rootfs!" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
CONSOLE=`readlink /proc/$$/fd/2`
|
||||
|
||||
if echo "$CONSOLE" | grep -q '^/dev/'; then
|
||||
[ -e /dev/kmsg ] || ln -s "$CONSOLE" /dev/kmsg
|
||||
[ -e /dev/console ] || ln -s "$CONSOLE" /dev/console
|
||||
else
|
||||
[ -e /dev/kmsg ] || ln -s /proc/self/fd/2 /dev/kmsg
|
||||
[ -e /dev/console ] || ln -s /dev/null /dev/console
|
||||
fi
|
||||
|
||||
|
||||
mount --make-private /
|
||||
mount --make-private /rom
|
||||
mount --make-private /overlay
|
||||
|
||||
if unshare -m --propagation unchanged true 2>/dev/null; then
|
||||
exec unshare -m --propagation unchanged /shadowrt/s1-unshared.sh "$CONSOLE"
|
||||
elif unshare -m --propagation shared true 2>/dev/null; then
|
||||
exec unshare -m --propagation shared /shadowrt/s1-unshared.sh "$CONSOLE"
|
||||
else
|
||||
echo "unshare does not support propagation flag, fallback to default" >&2
|
||||
exec unshare -m /shadowrt/s1-unshared.sh "$CONSOLE"
|
||||
fi
|
||||
Whitespace-only changes.
+32
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
|
||||
CFG=$1
|
||||
|
||||
IP_PROTO=dhcp
|
||||
IP_ADDRESS=
|
||||
|
||||
[ -n "$CFG" ] || CFG=/etc/board.json
|
||||
|
||||
. /lib/functions/uci-defaults.sh
|
||||
|
||||
board_config_update
|
||||
|
||||
json_is_a network object && exit 0
|
||||
|
||||
ucidef_set_hostname "iStoreOS"
|
||||
|
||||
ucidef_set_timezone "CST-8"
|
||||
|
||||
ucidef_set_ntpserver "0.cn.pool.ntp.org" "1.cn.pool.ntp.org" "2.cn.pool.ntp.org" "3.cn.pool.ntp.org"
|
||||
|
||||
if [ "static" = "$IP_PROTO" -a -n "$IP_ADDRESS" ]; then
|
||||
eval $(ipcalc.sh "$IP_ADDRESS")
|
||||
ucidef_set_interface_lan "eth0"
|
||||
ucidef_set_interface "lan" ipaddr "$IP" netmask "$NETMASK"
|
||||
else
|
||||
ucidef_set_interface_lan "eth0" "dhcp"
|
||||
fi
|
||||
|
||||
board_config_flush
|
||||
|
||||
[ -s "$CFG" ] || return 1
|
||||
@@ -0,0 +1,12 @@
|
||||
# This file is interpreted as shell script.
|
||||
# Put your custom iptables rules here, they will
|
||||
# be executed with each firewall (re-)start.
|
||||
|
||||
# Internal uci firewall chains are flushed and recreated on reload, so
|
||||
# put custom rules into the root chains e.g. INPUT or FORWARD or into the
|
||||
# special user chains, e.g. input_wan_rule or postrouting_lan_rule.
|
||||
|
||||
# workaround route shortcut issue in shadowrt clones on openwrt 22.03
|
||||
iptables -t mangle -I PREROUTING -p tcp -m conntrack --ctstate RELATED,ESTABLISHED -m socket -j ACCEPT || true
|
||||
ip6tables -t mangle -I PREROUTING -p tcp -m conntrack --ctstate RELATED,ESTABLISHED -m socket -j ACCEPT || true
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
set quickstart.main.disable_dhns=1
|
||||
commit quickstart
|
||||
EOF
|
||||
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
set argon.@global[0].primary='#5b7e99'
|
||||
set argon.@global[0].dark_primary='#337d8c'
|
||||
commit argon
|
||||
EOF
|
||||
|
||||
GATEWAY=
|
||||
DNS=
|
||||
DHCP_SERVER=
|
||||
|
||||
[ -n "$GATEWAY" ] && uci set network.lan.gateway="$GATEWAY"
|
||||
if [ -n "$DNS" ]; then
|
||||
for dns in $DNS; do
|
||||
uci add_list network.lan.dns="$dns"
|
||||
done
|
||||
fi
|
||||
uci commit network
|
||||
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
del dhcp.lan.ra
|
||||
del dhcp.lan.ra_slaac
|
||||
del dhcp.lan.ra_flags
|
||||
del dhcp.lan.dhcpv6
|
||||
EOF
|
||||
|
||||
if [ -z "$DHCP_SERVER" ]; then
|
||||
uci set dhcp.lan.ignore='1'
|
||||
fi
|
||||
uci commit dhcp
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,8 @@
|
||||
do_sysinfo_generic() {
|
||||
mkdir -p /tmp/sysinfo
|
||||
|
||||
echo "docker" > /tmp/sysinfo/board_name
|
||||
echo "Docker" > /tmp/sysinfo/model
|
||||
}
|
||||
|
||||
boot_hook_add preinit_main do_sysinfo_generic
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
echo "ignore '$0 $@' in container" >&2
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/usr/libexec/kmodloader
|
||||
@@ -0,0 +1 @@
|
||||
/usr/libexec/kmodloader
|
||||
Whitespace-only changes.
Whitespace-only changes.
Executable
+127
@@ -0,0 +1,127 @@
|
||||
# > getbootdisk
|
||||
# mmcblk0
|
||||
_getbootdisk()
|
||||
{
|
||||
local rootpart="`grep -Fm1 ' - squashfs /dev/root ' /proc/self/mountinfo | cut -d' ' -f3`"
|
||||
[ -z "$rootpart" ] && return 1
|
||||
local devpath="`readlink /sys/dev/block/$rootpart`"
|
||||
[ -z "$devpath" ] && return 1
|
||||
rootpart="${devpath##*/}"
|
||||
devpath="${devpath%%/$rootpart}"
|
||||
local rootdisk="${devpath##*/}"
|
||||
echo "$rootdisk"
|
||||
}
|
||||
|
||||
# > getbootdisk_lvm
|
||||
# dm-0
|
||||
_getbootdisk_lvm()
|
||||
{
|
||||
local rootpart
|
||||
if [ -e /rom/note ]; then
|
||||
rootpart="`grep -Fm1 ' / / ' /proc/self/mountinfo | grep -F ' - squashfs ' | cut -d' ' -f3`"
|
||||
else
|
||||
rootpart="`grep -Fm1 ' / /rom ' /proc/self/mountinfo | grep -F ' - squashfs ' | cut -d' ' -f3`"
|
||||
fi
|
||||
[ -z "$rootpart" ] && return 1
|
||||
local major=${rootpart%%:*}
|
||||
local minor=${rootpart##*:}
|
||||
minor="$(( $minor & 0xfffc ))"
|
||||
local devpath="`readlink /sys/dev/block/$major:$minor`"
|
||||
[ -z "$devpath" ] && return 1
|
||||
local rootdisk="${devpath##*/}"
|
||||
echo "$rootdisk"
|
||||
}
|
||||
|
||||
# > getpartofdisk sda 3
|
||||
# sda3
|
||||
# > getpartofdisk mmcblk0 3
|
||||
# mmcblk0p3
|
||||
_getpartofdisk()
|
||||
{
|
||||
local disk="$1" offset="$2" part
|
||||
if [[ "$offset" = 0 ]]; then
|
||||
echo "$disk"
|
||||
else
|
||||
part="$disk"
|
||||
echo "$part" | grep -q '^.*[0-9]$' && part="${part}p"
|
||||
part="${part}"$(( ${offset} ))
|
||||
if [ ! -b "/dev/$part" ]; then
|
||||
# lvm
|
||||
local line
|
||||
local MAJOR MINOR DEVNAME DEVTYPE
|
||||
while read line; do
|
||||
export -n "$line"
|
||||
done < "/sys/block/$disk/uevent"
|
||||
local devpath="`readlink /sys/dev/block/$MAJOR:$(($MINOR + $offset))`"
|
||||
if [ -n "$devpath" ]; then
|
||||
part="${devpath##*/}"
|
||||
fi
|
||||
fi
|
||||
echo "$part"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
_get_overlay_partition_default()
|
||||
{
|
||||
local bootdisk="`_getbootdisk`"
|
||||
[ -z "$bootdisk" ] && {
|
||||
log "getbootdisk failed, try lvm"
|
||||
bootdisk="`_getbootdisk_lvm`"
|
||||
}
|
||||
[ -z "$bootdisk" ] && {
|
||||
log "getbootdisk_lvm failed"
|
||||
return 1
|
||||
}
|
||||
if [ ! -e "/sys/block/$bootdisk/uevent" ]; then
|
||||
log "/sys/block/$bootdisk/uevent does not exist"
|
||||
return 1
|
||||
fi
|
||||
if [ -e /rom/note ]; then
|
||||
# we are in mount_root if /rom/note exists
|
||||
cat "/sys/block/$bootdisk/uevent" > /tmp/.bootdisk
|
||||
fi
|
||||
local overlay_dev="`_getpartofdisk $bootdisk 3`"
|
||||
[ -z "$overlay_dev" ] && {
|
||||
log "getpartofdisk $bootdisk 3 failed"
|
||||
return 1
|
||||
}
|
||||
OVERLAY_DEV="/dev/$overlay_dev"
|
||||
return 0
|
||||
}
|
||||
|
||||
_get_overlay_partition_fallback()
|
||||
{
|
||||
log "get_overlay_partition_fallback"
|
||||
rm -f /tmp/.bootdisk >/dev/null 2>&1
|
||||
local overlay_dev=$(
|
||||
. /lib/functions.sh
|
||||
. /lib/upgrade/common.sh
|
||||
export_bootdevice && export_partdevice overlay_dev 3 && echo $overlay_dev
|
||||
)
|
||||
[ -z "$overlay_dev" ] && return 1
|
||||
OVERLAY_DEV="/dev/$overlay_dev"
|
||||
return 0
|
||||
}
|
||||
|
||||
get_overlay_partition()
|
||||
{
|
||||
if [ -e /.dockerenv ]; then
|
||||
if mountpoint -q /overlay; then
|
||||
log "Overlay partition in Docker was mounted /overlay"
|
||||
OVERLAY_DEV="/overlay"
|
||||
OVERLAY_FS="DIR"
|
||||
FSTYPE="DIR"
|
||||
[ -f "/overlay/.reset" ] && FSTYPE="RESET"
|
||||
return 0
|
||||
else
|
||||
log "No overlay partition in Docker"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
_get_overlay_partition_default || _get_overlay_partition_fallback || {
|
||||
log "Unable to determine overlay partition"
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2022 jjm2473@gmail.com
|
||||
|
||||
DEFAULT_OVERLAY_FS="ext4"
|
||||
OVERLAY_LABEL="etc"
|
||||
|
||||
OVERLAY_FS=${DEFAULT_OVERLAY_FS}
|
||||
OVERLAY_DEV=
|
||||
|
||||
log() {
|
||||
echo "$1" >&2
|
||||
}
|
||||
|
||||
inlog() {
|
||||
$@ >&2
|
||||
}
|
||||
|
||||
. /lib/functions/istoreos-boot.sh
|
||||
|
||||
CONFIRM=n
|
||||
REBOOT=n
|
||||
|
||||
confirm()
|
||||
{
|
||||
[ "$1" = "y" ] && return 0
|
||||
echo "This will erase all settings and remove any installed packages. Are you sure? [N/y]"
|
||||
read CONFIRM
|
||||
[ "$CONFIRM" = "y" -o "$CONFIRM" = "Y" ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
do_reset_nofs()
|
||||
{
|
||||
get_overlay_partition || exit 1
|
||||
|
||||
if [ -z "$OVERLAY_DEV" ]; then
|
||||
echo "Unable to determine overlay partition" >&2
|
||||
return 1
|
||||
fi
|
||||
if [ "RESET" = "$FSTYPE" ]; then
|
||||
echo "Already resetted" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "DIR" = "$FSTYPE" ]; then
|
||||
touch /$OVERLAY_DEV/.reset
|
||||
sync /$OVERLAY_DEV
|
||||
return 0
|
||||
fi
|
||||
# if ! blkid -o device -t LABEL=$OVERLAY_LABEL "$OVERLAY_DEV" >/dev/null; then
|
||||
# echo "Overlay partition $OVERLAY_DEV not labeled $OVERLAY_LABEL" >&2
|
||||
# return 1
|
||||
# fi
|
||||
|
||||
echo "RESET000" | dd of="$OVERLAY_DEV" bs=512 count=1 conv=notrunc,sync,fsync 2>/dev/null
|
||||
echo 3 > /proc/sys/vm/drop_caches
|
||||
return 0
|
||||
}
|
||||
|
||||
do_reset_ext_overlay()
|
||||
{
|
||||
[ -d /ext_overlay ] && mountpoint -q /ext_overlay || return 0
|
||||
rm -f /ext_overlay/etc/.extroot-uuid
|
||||
touch /ext_overlay/.reset
|
||||
sync /ext_overlay
|
||||
}
|
||||
|
||||
do_reset()
|
||||
{
|
||||
if [ -f /.recovery_mode ]; then
|
||||
mount_root || ( echo "/overlay mount failed!" ; exit 1 )
|
||||
rm -rf /overlay/upper
|
||||
mkdir /overlay/upper
|
||||
sync /overlay
|
||||
else
|
||||
do_reset_nofs
|
||||
do_reset_ext_overlay
|
||||
fi
|
||||
if [ -f /lib/upgrade/keep.d/grub ]; then
|
||||
cat /lib/upgrade/keep.d/grub | while read; do
|
||||
rm -f "$REPLY"
|
||||
done
|
||||
fi
|
||||
[ "$REBOOT" = "y" ] && reboot
|
||||
exit 0
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
key="$1"
|
||||
case $key in
|
||||
-y) CONFIRM=y ;;
|
||||
-r) REBOOT=y ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
confirm $CONFIRM && do_reset
|
||||
exit 0
|
||||
+1
@@ -0,0 +1 @@
|
||||
/sbin/firstboot
|
||||
+549
@@ -0,0 +1,549 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2010 OpenWrt.org
|
||||
# Copyright (C) 2020-2023 jjm2473@gmail.com
|
||||
|
||||
DEFAULT_OVERLAY_FS="ext4"
|
||||
OVERLAY_LABEL="etc"
|
||||
|
||||
OVERLAY_FS=${DEFAULT_OVERLAY_FS}
|
||||
OVERLAY_DEV=
|
||||
|
||||
log() {
|
||||
echo "<1>mount_root: $1" >/dev/kmsg
|
||||
}
|
||||
|
||||
inlog() {
|
||||
$@ >/dev/kmsg 2>&1
|
||||
}
|
||||
|
||||
. /lib/functions/istoreos-boot.sh
|
||||
|
||||
validate_overlay_partition()
|
||||
{
|
||||
log "- mount overlayfs -"
|
||||
|
||||
# Check device existence
|
||||
[ ! -e "$OVERLAY_DEV" ] && \
|
||||
log "overlay: $OVERLAY_DEV not found" && return 1
|
||||
|
||||
[ -n "$FSTYPE" ] && return 0
|
||||
|
||||
# get filesystem
|
||||
if dd if="$OVERLAY_DEV" bs=5 count=1 2>/dev/null | grep -qF RESET; then
|
||||
FSTYPE=RESET
|
||||
log "overlay: $OVERLAY_DEV marked as RESET, format later"
|
||||
return 0
|
||||
elif blkid -o device -t LABEL=$OVERLAY_LABEL "$OVERLAY_DEV" >/dev/null; then
|
||||
FSTYPE=`blkid -o value -s TYPE $OVERLAY_DEV`
|
||||
else
|
||||
FSTYPE=RESET
|
||||
log "overlay: $OVERLAY_DEV not labeled, format later"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -z "$FSTYPE" ]; then
|
||||
log "overlay: no valid filesystem"
|
||||
else
|
||||
log "overlay: filesystem $FSTYPE found"
|
||||
fi
|
||||
}
|
||||
|
||||
format_overlay_partition()
|
||||
{
|
||||
local RET
|
||||
[ "$FSTYPE" = "$OVERLAY_FS" ] && return 0
|
||||
|
||||
log "overlay: try to format $OVERLAY_DEV as $OVERLAY_FS partition"
|
||||
|
||||
case "$OVERLAY_FS" in
|
||||
ext4)
|
||||
mkfs.ext4 -q -j -FF -L $OVERLAY_LABEL "$OVERLAY_DEV"
|
||||
RET=$?
|
||||
[ $RET -ne 0 ] || tune2fs -o '^user_xattr,^acl' -r 1024 "$OVERLAY_DEV"
|
||||
;;
|
||||
DIR)
|
||||
rm -rf "$OVERLAY_DEV/upper" "$OVERLAY_DEV/work" "$OVERLAY_DEV/.reset"
|
||||
RET=0
|
||||
;;
|
||||
*)
|
||||
log "overlay: unhandled fs type : $OVERLAY_FS"
|
||||
RET=1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ $RET -ne 0 ]; then
|
||||
log "overlay: format command failed ret: $RET"
|
||||
else
|
||||
echo 3 > /proc/sys/vm/drop_caches
|
||||
FSTYPE=$OVERLAY_FS
|
||||
fi
|
||||
|
||||
return $RET
|
||||
}
|
||||
|
||||
check_overlay_partition()
|
||||
{
|
||||
case "$FSTYPE" in
|
||||
ext4)
|
||||
fsck.ext4 -y "$OVERLAY_DEV" >/dev/null
|
||||
sync
|
||||
resize2fs "$OVERLAY_DEV"
|
||||
sync
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
mount_overlay_partition()
|
||||
{
|
||||
local RET
|
||||
[ "DIR" = "$OVERLAY_FS" ] && return 0
|
||||
mount -t $OVERLAY_FS "$OVERLAY_DEV" /overlay
|
||||
RET=$?
|
||||
|
||||
[ $RET -ne 0 ] && \
|
||||
log "overlay: failed to mount $OVERLAY_DEV as $OVERLAY_FS partition ret: $RET" && return $RET
|
||||
|
||||
log 'overlay: successfully mounted'
|
||||
}
|
||||
|
||||
OVERLAYFS_XATTR=""
|
||||
is_overlayfs_use_xattr() {
|
||||
# only linux 4.13 or above
|
||||
[ -z "$OVERLAYFS_XATTR" ] && OVERLAYFS_XATTR=$([ "0$((`uname -r | cut -d. -f1-2 | sed 's/\./ * 100 + /'`))" -ge 413 ] && echo 1 || echo 0)
|
||||
[ "$OVERLAYFS_XATTR" = "1" ]
|
||||
}
|
||||
|
||||
ROOTFS_UUID=""
|
||||
rootfs_uuid() {
|
||||
if [ -z "$ROOTFS_UUID" ]; then
|
||||
ROOTFS_UUID=`block info | grep -Fw 'MOUNT="/"' | sed -E 's/.* UUID="([^"]+)" .*/\1/'`
|
||||
[ -z "$ROOTFS_UUID" ] && [ -e /.dockerenv ] && ROOTFS_UUID=rel-`md5sum /etc/openwrt_release | cut -d' ' -f1`
|
||||
[ -z "$ROOTFS_UUID" ] && ROOTFS_UUID="UNKNOWN"
|
||||
fi
|
||||
echo "$ROOTFS_UUID"
|
||||
}
|
||||
|
||||
update_overlay_lower_uuid() {
|
||||
local realuuid=`rootfs_uuid`
|
||||
local lastuuid="NULL"
|
||||
if [ -f "$1/.rootfs-uuid" ]; then
|
||||
lastuuid=`cat "$1/.rootfs-uuid"`
|
||||
fi
|
||||
if [ "$realuuid" != "$lastuuid" ]; then
|
||||
echo "$realuuid" >"$1/.rootfs-uuid"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
clean_overlayfs_origin() {
|
||||
local line
|
||||
cd "$1"
|
||||
getfattr -R -P -h --match='trusted.overlay.origin' . 2>/dev/null | grep '^# file: ' | cut -c9- | while read; do
|
||||
line="$REPLY"
|
||||
setfattr -x "trusted.overlay.origin" -h "$line" 2>/dev/null
|
||||
done
|
||||
cd - >/dev/null 2>&1
|
||||
}
|
||||
|
||||
clean_overlayfs_redundant_opaque() {
|
||||
local line
|
||||
cd "$1"
|
||||
getfattr -R -P -h --match='trusted.overlay.opaque' . 2>/dev/null | grep '^# file: ' | cut -c9- | while read; do
|
||||
line="$REPLY"
|
||||
[ -z "$line" -o -d "/$line" ] && continue
|
||||
setfattr -x "trusted.overlay.opaque" -h "$line" 2>/dev/null
|
||||
done
|
||||
cd - >/dev/null 2>&1
|
||||
}
|
||||
|
||||
clean_overlayfs_redundant_whiteout_marker()
|
||||
{
|
||||
local line
|
||||
cd "$1"
|
||||
find upper -xdev -path 'upper/opt' -prune ! -type d -o -type c -perm 0 -user 0 -group 0 | sed 's#^upper/#/#g' | while read; do
|
||||
line="$REPLY"
|
||||
[ ! -e "$line" -a \( -z "$2" -o ! -e "$2/upper$line" \) ] && \
|
||||
rm -f "upper$line"
|
||||
done
|
||||
cd - >/dev/null 2>&1
|
||||
}
|
||||
|
||||
clean_overlayfs_redundant_whiteout_marker_luci()
|
||||
{
|
||||
local line
|
||||
[ -d "$1/upper/usr/lib/lua/luci" ] || return 0
|
||||
cd "$1/upper/usr/lib/lua"
|
||||
find luci -xdev -type c -perm 0 -user 0 -group 0 | while read; do
|
||||
line="$REPLY"
|
||||
[ ! -e "/usr/lib/lua/$line" -a \( -z "$2" -o ! -e "$2/upper/usr/lib/lua/$line" \) ] && \
|
||||
rm -f "$line"
|
||||
done
|
||||
cd - >/dev/null 2>&1
|
||||
}
|
||||
|
||||
clean_os_version_files()
|
||||
{
|
||||
cd "$1"
|
||||
rm -f upper/etc/openwrt_release \
|
||||
upper/etc/openwrt_version \
|
||||
upper/usr/lib/os-release
|
||||
cd - >/dev/null 2>&1
|
||||
}
|
||||
|
||||
nonconf_files_of_pkg() {
|
||||
local pkgname="$1"
|
||||
if [ -s /usr/lib/opkg/info/${pkgname}.conffiles ]; then
|
||||
grep -hvxFf /usr/lib/opkg/info/${pkgname}.conffiles \
|
||||
/usr/lib/opkg/info/${pkgname}.list
|
||||
else
|
||||
cat /usr/lib/opkg/info/${pkgname}.list
|
||||
fi
|
||||
}
|
||||
|
||||
clean_pkg_content() {
|
||||
local overlay="$1"
|
||||
local pkgname="$2"
|
||||
local line
|
||||
|
||||
if [ -f /usr/lib/opkg/info/${pkgname}.control ]; then
|
||||
if [ -f /usr/lib/opkg/info/${pkgname}.list ]; then
|
||||
nonconf_files_of_pkg "${pkgname}" | grep -v '^/etc/uci-defaults/' | while read; do
|
||||
line="$REPLY"
|
||||
rm -f "$overlay/upper/${line}"
|
||||
done
|
||||
rm -f $overlay/upper/usr/lib/opkg/info/${pkgname}.list
|
||||
fi
|
||||
rm -f $overlay/upper/usr/lib/opkg/info/${pkgname}.*
|
||||
fi
|
||||
}
|
||||
|
||||
pkg_depends_rec() {
|
||||
local pkgname="$1"
|
||||
local line
|
||||
local dep
|
||||
local deps
|
||||
|
||||
echo "${pkgname}" | grep -q '^kmod-' && return
|
||||
|
||||
if [ -f /usr/lib/opkg/info/${pkgname}.control -a ! -f /tmp/reset_core_packages/${pkgname} ]; then
|
||||
deps=`grep '^Depends: ' /usr/lib/opkg/info/${pkgname}.control | sed -e 's/^Depends: //' -e 's/ ([^)]*)//g' -e 's/,/ /g'`
|
||||
touch /tmp/reset_core_packages/${pkgname}
|
||||
for dep in $deps ; do
|
||||
pkg_depends_rec "$dep"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
list_core_packages()
|
||||
{
|
||||
local pkg
|
||||
mkdir -p /tmp/reset_core_packages
|
||||
for pkg in \
|
||||
luci-compat luci-lua-runtime luci-lib-base luci-base uhttpd luci-ssl-openssl luci-ssl \
|
||||
rpcd procd \
|
||||
netifd dnsmasq-full dnsmasq firewall4 \
|
||||
block-mount \
|
||||
curl \
|
||||
ttyd dropbear kernel
|
||||
do
|
||||
pkg_depends_rec $pkg
|
||||
done
|
||||
}
|
||||
|
||||
reset_core_packages()
|
||||
{
|
||||
local overlay="$1"
|
||||
local running_ver=$(grep 'DISTRIB_RELEASE=' /etc/openwrt_release 2>/dev/null | sed -E "s/.*='(.+)'.*/\1/")
|
||||
[ "v$(cat "$overlay/.upgrading" 2>/dev/null)" = "v$running_ver" ] && return
|
||||
local pkg
|
||||
[ -d /tmp/reset_core_packages ] || list_core_packages
|
||||
echo "Reset packages in $overlay: `ls /tmp/reset_core_packages/ | xargs echo`" | dd bs=960 2>/dev/null
|
||||
ls /tmp/reset_core_packages/ | while read; do
|
||||
pkg="$REPLY"
|
||||
clean_pkg_content "$overlay" $pkg
|
||||
done
|
||||
}
|
||||
|
||||
clean_kmods()
|
||||
{
|
||||
local DUMMY_VERSION=0.0.0-r1
|
||||
if [ -d "$1/upper/usr/lib/opkg/info" ]; then
|
||||
local compat=`grep -m1 '^Version: ' /usr/lib/opkg/info/kernel.control | sed 's/^Version: //'`
|
||||
if [ -n "$compat" ]; then
|
||||
cd "$1/upper/usr/lib/opkg/info"
|
||||
# ignore processed
|
||||
grep -Lm1 '^Version: '$DUMMY_VERSION'$' kmod-*.control 2>/dev/null | sed 's/.control$//' > /tmp/cand_kmods.txt
|
||||
|
||||
if [ -s /tmp/cand_kmods.txt ]; then
|
||||
grep -LFm1 "kernel (=$compat)" kmod-*.control | sed 's/.control$//' | grep -Fxf /tmp/cand_kmods.txt > /tmp/del_kmods.txt
|
||||
grep -lFm1 "kernel (=$compat)" kmod-*.control | sed 's/.control$//' | grep -Fxf /tmp/cand_kmods.txt > /tmp/safe_kmods.txt
|
||||
else
|
||||
> /tmp/del_kmods.txt
|
||||
fi
|
||||
rm -f /tmp/cand_kmods.txt
|
||||
|
||||
# remove duplicated
|
||||
if [ -s /tmp/safe_kmods.txt ]; then
|
||||
( cd /usr/lib/opkg/info && ls kmod-*.control 2>/dev/null | sed 's/.control$//' ) | grep -Fxf /tmp/safe_kmods.txt >> /tmp/del_kmods.txt
|
||||
fi
|
||||
rm -f /tmp/safe_kmods.txt
|
||||
|
||||
if [ -s /tmp/del_kmods.txt ]; then
|
||||
echo "Clean kmods in $1: `cat /tmp/del_kmods.txt | xargs echo`" | dd bs=960 2>/dev/null
|
||||
|
||||
cat /tmp/del_kmods.txt | sed 's/$/.list/' | xargs cat | sed "s#^/#$1/upper/#" | xargs -r rm -f
|
||||
cat /tmp/del_kmods.txt | xargs -rn1 sh -c 'rm -f $0.list $0.prerm $0.postinst'
|
||||
cat /tmp/del_kmods.txt | sed 's/$/.control/' | xargs -r sed -i 's/^Version: .*$/Version: '$DUMMY_VERSION'/g'
|
||||
fi
|
||||
rm -f /tmp/del_kmods.txt
|
||||
cd - >/dev/null 2>&1
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
|
||||
# rm -rf $1/upper/lib/modules $1/upper/etc/modules-boot.d \
|
||||
# $1/upper/etc/modules.d $1/upper/usr/lib/opkg/info/kmod-* \
|
||||
# $1/upper/usr/lib/opkg/info/kernel.* 2>/dev/null
|
||||
}
|
||||
|
||||
prepare_primary_overlay()
|
||||
{
|
||||
if [ -f /overlay/.reset ]; then
|
||||
rm -rf /overlay/upper /overlay/work
|
||||
rm -f /overlay/.reset
|
||||
sync /overlay
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_primary_overlay_after_merge()
|
||||
{
|
||||
if [ -d /overlay/upper ]; then
|
||||
if update_overlay_lower_uuid /overlay || [ -f /overlay/.upgrading ] ; then
|
||||
clean_os_version_files /overlay
|
||||
reset_core_packages /overlay
|
||||
touch /overlay/.upgrading
|
||||
fi
|
||||
if [ -f /overlay/.upgrading -o -f /overlay/.merged ] ; then
|
||||
if is_overlayfs_use_xattr ; then
|
||||
clean_overlayfs_origin /overlay/upper
|
||||
clean_overlayfs_redundant_opaque /overlay/upper
|
||||
fi
|
||||
clean_overlayfs_redundant_whiteout_marker /overlay
|
||||
else
|
||||
clean_overlayfs_redundant_whiteout_marker_luci /overlay
|
||||
fi
|
||||
else
|
||||
update_overlay_lower_uuid /overlay
|
||||
fi
|
||||
if [ -f /overlay/upper/usr/lib/opkg/.upgrading ]; then
|
||||
clean_kmods /overlay
|
||||
fi
|
||||
|
||||
rm -f /overlay/.upgrading /overlay/.merged
|
||||
|
||||
mkdir -p /overlay/upper /overlay/work
|
||||
}
|
||||
|
||||
pivot_root_to_overlayfs()
|
||||
{
|
||||
# pivot
|
||||
mount --move /proc /mnt/proc
|
||||
|
||||
pivot_root /mnt /mnt/rom || return 1
|
||||
|
||||
mount --move /rom/dev /dev
|
||||
mount --move /rom/tmp /tmp
|
||||
mount --move /rom/sys /sys
|
||||
mount --move /rom/overlay /overlay
|
||||
}
|
||||
|
||||
mount_extroot()
|
||||
{
|
||||
[ "DIR" = "$OVERLAY_FS" ] && return 1
|
||||
[ -f /overlay/upper/etc/config/fstab ] || return 1
|
||||
# let `block extroot` read /overlay/upper/etc/config/fstab
|
||||
mount --bind /overlay/upper/etc/config /etc/config || return 1
|
||||
block extroot
|
||||
local ret=$?
|
||||
umount /etc/config
|
||||
return $ret
|
||||
}
|
||||
|
||||
apply_overlayfs_opaque() {
|
||||
local line
|
||||
local dest="$2"
|
||||
cd "$1"
|
||||
if is_overlayfs_use_xattr; then
|
||||
getfattr -R -P -h --match='trusted.overlay.opaque' upper 2>/dev/null | grep '^# file: ' | cut -c9- | while read; do
|
||||
line="$REPLY"
|
||||
rm -rf "$dest/$line"
|
||||
done
|
||||
fi
|
||||
find upper -xdev -type c | while read; do
|
||||
line="$REPLY"
|
||||
rm -rf "$dest/$line"
|
||||
done
|
||||
cd - >/dev/null 2>&1
|
||||
}
|
||||
|
||||
prepare_ext_overlay()
|
||||
{
|
||||
local newoverlaydir=$1
|
||||
local overlaydir=$2
|
||||
|
||||
if [ -f "$newoverlaydir/.reset" ]; then
|
||||
rm -rf "$newoverlaydir/upper"
|
||||
rm -f "$newoverlaydir/.commit" "$newoverlaydir/.reset"
|
||||
elif [ -f "$newoverlaydir/.commit" ]; then
|
||||
if [ -d "$newoverlaydir/upper" ]; then
|
||||
apply_overlayfs_opaque "$newoverlaydir" "$overlaydir"
|
||||
fi
|
||||
cp -a "$newoverlaydir/upper/." "$overlaydir/upper" && {
|
||||
touch "$overlaydir/.merged"
|
||||
rmdir "$overlaydir/upper/ext_overlay"
|
||||
rm -rf "$newoverlaydir/upper"
|
||||
mkdir -m 0755 -p "$newoverlaydir/upper/ext_overlay"
|
||||
}
|
||||
rm -f "$newoverlaydir/.commit"
|
||||
else
|
||||
prepare_primary_overlay_after_merge
|
||||
touch "$overlaydir/.clean"
|
||||
if [ -f "$newoverlaydir/upper/usr/lib/opkg/.upgrading" ]; then
|
||||
clean_kmods "$newoverlaydir"
|
||||
fi
|
||||
if [ -f "$newoverlaydir/.upgrading" ]; then
|
||||
clean_os_version_files "$newoverlaydir"
|
||||
reset_core_packages "$newoverlaydir"
|
||||
is_overlayfs_use_xattr && clean_overlayfs_origin "$newoverlaydir/upper"
|
||||
fi
|
||||
if [ -d "$newoverlaydir/upper" -a \( -f "$newoverlaydir/upper/usr/lib/opkg/.upgrading" -o ! -f "$overlaydir/.sandbox" \) ]; then
|
||||
clean_overlayfs_redundant_whiteout_marker "$newoverlaydir" "$overlaydir"
|
||||
else
|
||||
clean_overlayfs_redundant_whiteout_marker_luci "$newoverlaydir" "$overlaydir"
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f "$newoverlaydir/.upgrading"
|
||||
|
||||
( mkdir -m 0755 -p $newoverlaydir/upper/ext_overlay && \
|
||||
mkdir -m 0755 -p $newoverlaydir/work ) || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
prepare_mnt()
|
||||
{
|
||||
rm -rf /tmp/reset_core_packages
|
||||
prepare_primary_overlay
|
||||
|
||||
if mount_extroot && mountpoint -q /tmp/extroot/mnt; then
|
||||
local dir
|
||||
for dir in proc dev tmp sys overlay rom root; do
|
||||
mkdir -m 0755 /tmp/extroot/mnt/$dir
|
||||
done
|
||||
if mount --move /tmp/extroot/mnt /mnt; then
|
||||
# writeable extroot existed, we don't need overlay
|
||||
umount /tmp/extroot/overlay
|
||||
return 0
|
||||
fi
|
||||
umount /tmp/extroot/mnt
|
||||
fi
|
||||
|
||||
local lowerdir=/
|
||||
local overlaydir=/overlay
|
||||
|
||||
if mountpoint -q /tmp/extroot/overlay; then
|
||||
# use ext overlay as upper layer
|
||||
local newoverlaydir=/tmp/extroot/overlay
|
||||
if [ `mountpoint -x "$OVERLAY_DEV"` != `mountpoint -d $newoverlaydir` ] && \
|
||||
prepare_ext_overlay $newoverlaydir $overlaydir && \
|
||||
mount --move /tmp/extroot/overlay /ext_overlay; then
|
||||
lowerdir=$overlaydir/upper:$lowerdir
|
||||
overlaydir=/ext_overlay
|
||||
else
|
||||
umount /tmp/extroot/overlay
|
||||
fi
|
||||
fi
|
||||
if [ -f /overlay/.clean ]; then
|
||||
rm -f /overlay/.clean
|
||||
else
|
||||
prepare_primary_overlay_after_merge
|
||||
fi
|
||||
|
||||
rm -rf /tmp/reset_core_packages
|
||||
|
||||
mount -o noatime,lowerdir=$lowerdir,upperdir=$overlaydir/upper,workdir=$overlaydir/work \
|
||||
-t overlay "overlayfs:/overlay" /mnt \
|
||||
|| return 1
|
||||
|
||||
if [ "$overlaydir" = "/ext_overlay" ]; then
|
||||
[ -f "/overlay/.sandbox" ] || touch "/overlay/.sandbox"
|
||||
else
|
||||
rm -f "/overlay/.sandbox"
|
||||
fi
|
||||
|
||||
sync
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
post_extroot()
|
||||
{
|
||||
mountpoint -q /rom/ext_overlay && mount --move /rom/ext_overlay /ext_overlay
|
||||
mountpoint -q /tmp/extroot/overlay && umount /tmp/extroot/overlay
|
||||
rmdir /tmp/extroot/overlay
|
||||
mountpoint -q /tmp/extroot/mnt && umount /tmp/extroot/mnt
|
||||
rmdir /tmp/extroot/mnt
|
||||
rmdir /tmp/extroot
|
||||
mountpoint -q /rom/dev && umount /rom/dev
|
||||
}
|
||||
|
||||
do_mount_overlayfs()
|
||||
{
|
||||
local SHAREDMNT=false
|
||||
[ ! -e /rom/note ] && return 0
|
||||
|
||||
inlog get_overlay_partition || return 1
|
||||
|
||||
inlog validate_overlay_partition || return 1
|
||||
|
||||
inlog format_overlay_partition || return 1
|
||||
|
||||
inlog check_overlay_partition
|
||||
|
||||
inlog mount_overlay_partition || return 1
|
||||
|
||||
# if we are in recovery mode, just skip pivot_root
|
||||
if [ -f /.recovery_mode ]; then
|
||||
rm -f /rom/note >/dev/null 2>&1
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -e /.dockerenv ] && mountpoint -q /mnt; then
|
||||
SHAREDMNT=true
|
||||
mkdir /dev/mnt
|
||||
mount --move /mnt /dev/mnt
|
||||
fi
|
||||
|
||||
inlog prepare_mnt || return 1
|
||||
|
||||
inlog pivot_root_to_overlayfs || return 1
|
||||
|
||||
post_extroot >/dev/null 2>&1
|
||||
|
||||
if $SHAREDMNT; then
|
||||
mount --rbind /dev/mnt /mnt
|
||||
mount --make-rprivate /dev/mnt
|
||||
umount -R /dev/mnt || /bin/umount -r -l /dev/mnt
|
||||
rmdir /dev/mnt
|
||||
else
|
||||
inlog mount --make-shared -t tmpfs -o size=4M tmpfs /mnt
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
[ -n "$1" -a "$1" != "start" ] || ( do_mount_overlayfs && exit 0 )
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
mkdir -p /var/lock
|
||||
|
||||
for service in adb-enablemodem appfilter dockerd gpio_switch hd-idle \
|
||||
kmods kmods-unload lcdsimple led lm-sensors luci-fan \
|
||||
mdadm modemmanager odhcpd smartd sysfixtime sysfsutils sysntpd \
|
||||
tuning_net umount usbmode usbmuxd wan_drop zprintk zram
|
||||
do
|
||||
[ -x /etc/init.d/$service ] && /etc/init.d/$service disable
|
||||
done
|
||||
|
||||
for file in \
|
||||
/etc/board.d \
|
||||
/etc/config/appfilter \
|
||||
/etc/config/kmods \
|
||||
/etc/init.d/appfilter \
|
||||
/etc/init.d/umount \
|
||||
/ext_overlay \
|
||||
/lib/upgrade/ota.sh \
|
||||
/lib/upgrade/platform.sh \
|
||||
/lib/board \
|
||||
/sbin/ujail \
|
||||
/usr/lib/opkg/info/luci-app-oaf.control \
|
||||
/usr/libexec/fan-control \
|
||||
/usr/sbin/sandbox
|
||||
do
|
||||
rm -rf "$file"
|
||||
done
|
||||
|
||||
rm -rf /var/lock/*.lock
|
||||
@@ -0,0 +1,149 @@
|
||||
#!/bin/sh
|
||||
NEWROOT=/rom
|
||||
PATCHROM=/tmp/rom
|
||||
|
||||
SHAREDMNT=false
|
||||
mountpoint -q /mnt && SHAREDMNT=true
|
||||
|
||||
mount --make-private /
|
||||
mount --make-private /sys
|
||||
mount --make-rprivate /tmp 2>/dev/null
|
||||
mount --make-rprivate /proc
|
||||
mount --make-private /rom
|
||||
mount --make-private /overlay
|
||||
|
||||
mount -t tmpfs -o ro,relatime tmpfs /sys/block
|
||||
mount -t tmpfs -o size=1M tmpfs /tmp
|
||||
|
||||
# make all private except /mnt
|
||||
mkdir -p $PATCHROM
|
||||
mount --bind / $PATCHROM
|
||||
mount --move /proc $PATCHROM/proc
|
||||
# move /mnt to tmproot/mnt
|
||||
$SHAREDMNT && mount --move /mnt $PATCHROM/mnt
|
||||
pivot_root $PATCHROM $PATCHROM/tmp || exit 1
|
||||
|
||||
# old root now in /tmp
|
||||
mount --make-rprivate /tmp
|
||||
$SHAREDMNT && mount --move /mnt /tmp/mnt
|
||||
mount --move /proc /tmp/proc
|
||||
pivot_root /tmp /tmp$PATCHROM || exit 1
|
||||
umount $PATCHROM
|
||||
|
||||
# umount annoying docker rootfs
|
||||
if $SHAREDMNT; then
|
||||
OLDROOTFS=`grep -m1 "^overlay / overlay" /proc/1/mounts | sed -nE 's#^.*,workdir=(/mnt/.*)/work 0 0$#\1/merged#p'`
|
||||
if [ -n "$OLDROOTFS" ]; then
|
||||
echo "umount $OLDROOTFS" >&2
|
||||
mount --make-rprivate "$OLDROOTFS"
|
||||
umount -l "$OLDROOTFS"
|
||||
fi
|
||||
fi
|
||||
|
||||
# patch rom
|
||||
LOWERDIR=/rom
|
||||
|
||||
mkdir -p /tmp/tmp/shadowrt /tmp/patch/upper/usr/libexec /tmp/patch/work
|
||||
|
||||
cp -a /rom/sbin/kmodloader /tmp/patch/upper/usr/libexec/
|
||||
|
||||
for o in 0 2; do
|
||||
cp -a /shadowrt/overwrite/$o/. /tmp/patch/upper/
|
||||
done
|
||||
|
||||
OVERLAYFS_UUID=",uuid=null"
|
||||
if ! mount -o noatime,lowerdir=$LOWERDIR,upperdir=/tmp/patch/upper,workdir=/tmp/patch/work,xino=off$OVERLAYFS_UUID \
|
||||
-t overlay "/dev/root" $PATCHROM ; then
|
||||
OVERLAYFS_UUID=""
|
||||
mount -o noatime,lowerdir=$LOWERDIR,upperdir=/tmp/patch/upper,workdir=/tmp/patch/work,xino=off \
|
||||
-t overlay "/dev/root" $PATCHROM || exit 1
|
||||
fi
|
||||
|
||||
mount --bind /tmp/tmp $PATCHROM/tmp
|
||||
mount --bind /shadowrt $PATCHROM/tmp/shadowrt
|
||||
chroot $PATCHROM /tmp/shadowrt/s1-chroot-patchrom.sh
|
||||
umount $PATCHROM/tmp/shadowrt
|
||||
umount $PATCHROM/tmp
|
||||
rm -rf /tmp/tmp
|
||||
|
||||
set_default_ip()
|
||||
{
|
||||
local ip=$1
|
||||
[ -n "$ip" ] || return 1
|
||||
echo "pass ip address to shadow: $ip" >&2
|
||||
sed -i -e "s/^IP_PROTO=.*/IP_PROTO=static/g" -e "s#^IP_ADDRESS=.*#IP_ADDRESS=$ip#g" "$PATCHROM/bin/board_detect"
|
||||
}
|
||||
|
||||
set_default_gw_dns()
|
||||
{
|
||||
local gw=$1
|
||||
local dns="$2"
|
||||
echo "pass gateway/dns to shadow: $gw/$dns" >&2
|
||||
sed -i -e "s/^GATEWAY=.*/GATEWAY=$gw/g" -e "s/^DNS=.*/DNS='$dns'/g" "$PATCHROM/etc/uci-defaults/zzz-dockerenv"
|
||||
}
|
||||
|
||||
set_default_network()
|
||||
{
|
||||
if [ "dhcp" != "$IP_PROTO" ]; then
|
||||
# ip
|
||||
local ip=$IP_ADDRESS
|
||||
[ -z "$ip" ] && ip=`ip addr show dev eth0 | grep -m1 'inet ' | head -1 | sed -nE 's#.*inet ([0-9\.]*)/([0-9]*) .*#\1/\2#p'`
|
||||
set_default_ip "$ip"
|
||||
# gateway dns
|
||||
local gw dns
|
||||
gw=$IP_GATEWAY
|
||||
dns="$IP_DNS"
|
||||
[ -z "$gw" ] && gw="$(route -n | grep -m1 '^0\.0\.0\.0 .* eth0$' | head -1 | xargs -r sh -c 'echo $1')"
|
||||
[ -z "$dns" ] && dns="$(grep '^nameserver ' /etc/resolv.conf | grep -oE '\d+\.\d+\.\d+\.\d+' | xargs -r sh -c 'echo $@' ignored)"
|
||||
set_default_gw_dns "$gw" "$dns"
|
||||
|
||||
[ "on" = "$DHCP_SERVER" ] && sed -i -e "s/^DHCP_SERVER=.*/DHCP_SERVER=1/g" "$PATCHROM/etc/uci-defaults/zzz-dockerenv"
|
||||
fi
|
||||
}
|
||||
|
||||
set_default_network
|
||||
|
||||
export -n IP_PROTO IP_ADDRESS IP_GATEWAY IP_DNS DHCP_SERVER
|
||||
unset IP_PROTO IP_ADDRESS IP_GATEWAY IP_DNS DHCP_SERVER
|
||||
|
||||
umount $PATCHROM
|
||||
|
||||
|
||||
# make sysctl net.* writable
|
||||
mount --bind -o rw /proc/sys/net /proc/sys/net
|
||||
# workaround busybox mount bug (alpine 3.22.2 arm64/v8)
|
||||
grep '^proc /proc' /proc/mounts > /tmp/proconly.txt
|
||||
sh -c 'mount --bind /tmp/proconly.txt /proc/$$/mounts && exec mount -o rw,remount /proc/sys/net'
|
||||
rm -f /tmp/proconly.txt
|
||||
|
||||
|
||||
PROCD_VER=`grep -o '^Version: [0-9]*' /rom/usr/lib/opkg/info/procd.control | cut -d' ' -f2`
|
||||
if [ -n "$PROCD_VER" -a "$PROCD_VER" -lt 2024 ]; then
|
||||
# workaround procd < 2024 infite loop on resolving rootfs type, e.g. `ubus call system board`
|
||||
echo "overwrite /proc/$$/mounts for bug on procd v$PROCD_VER" >&2
|
||||
echo "/dev/root /rom squashfs ro,relatime 0 0" > /tmp/procd_mounts.txt
|
||||
cat /proc/mounts | grep -v '^tmpfs /tmp tmpfs' | grep -v ' /shadowrt ' >> /tmp/procd_mounts.txt
|
||||
mount --bind /tmp/procd_mounts.txt /proc/$$/mounts
|
||||
rm -f /tmp/procd_mounts.txt
|
||||
fi
|
||||
|
||||
|
||||
# mount openwrt rootfs
|
||||
mount -o ro,noatime,lowerdir=/tmp/patch/upper:$LOWERDIR,xino=off$OVERLAYFS_UUID \
|
||||
-t overlay "/dev/root" /rom || exit 1
|
||||
|
||||
umount /tmp
|
||||
|
||||
mount --bind -o private /overlay $NEWROOT/overlay
|
||||
$SHAREDMNT && mount --move /mnt $NEWROOT/mnt
|
||||
|
||||
mount --move /dev $NEWROOT/dev
|
||||
mount --move /sys $NEWROOT/sys
|
||||
mount --move /shadowrt $NEWROOT/tmp/busy
|
||||
|
||||
mount --move /proc $NEWROOT/proc
|
||||
|
||||
pivot_root $NEWROOT $NEWROOT/tmp/oldroot || exit 1
|
||||
cd /
|
||||
exec /tmp/busy/s2-wrtroot.sh "$1" "/tmp/oldroot"
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd /
|
||||
umount -R $2 || umount -l $2
|
||||
umount -l /tmp/busy
|
||||
|
||||
if echo "$1" | grep -q '^/dev/' ; then
|
||||
/bin/sh -i </dev/console >/dev/console 2>&1 &
|
||||
fi
|
||||
|
||||
echo "exec /sbin/init" >&2
|
||||
|
||||
export -n TZ SHLVL HOSTNAME OLDPWD PWD USER LOGNAME
|
||||
unset TZ SHLVL HOSTNAME OLDPWD PWD USER LOGNAME
|
||||
|
||||
export HOME=/ TERM=linux PATH=/sbin:/bin
|
||||
|
||||
exec /sbin/init </dev/null
|
||||
|
||||
Reference in new issue
Block a user