feat: поддержка HTTP/HTTPS прокси (#45), комплексная доработка деинсталляции и отката конфигурации (1.3.22)
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
#!/usr/bin/env ucode
|
||||
|
||||
// Tachyon Clean Uninstaller Module
|
||||
// Fully restores dnsmasq / dhcp, flushes nftables & policy routing,
|
||||
// removes Tachyon packages & crontab hooks, restores Forkop/Podkop if present,
|
||||
// and safely uninstalls Tachyon without leaving lingering state on OpenWrt.
|
||||
|
||||
let fs = require("fs");
|
||||
let common = require("core.common");
|
||||
let as_string = common.as_string;
|
||||
|
||||
function run_cmd(args) {
|
||||
let parts = [];
|
||||
for (let arg in args)
|
||||
push(parts, "'" + replace(as_string(arg), /'/g, "'\\''") + "'");
|
||||
return system(join(" ", parts) + " >/dev/null 2>&1");
|
||||
}
|
||||
|
||||
function run_shell(cmd) {
|
||||
return system(cmd + " >/dev/null 2>&1");
|
||||
}
|
||||
|
||||
function log_step(msg) {
|
||||
print("[Tachyon Uninstall] ", msg, "\n");
|
||||
}
|
||||
|
||||
function remove_file(path) {
|
||||
if (fs.stat(path) != null)
|
||||
fs.unlink(path);
|
||||
}
|
||||
|
||||
function remove_tree(path) {
|
||||
if (fs.stat(path) != null)
|
||||
run_shell("rm -rf '" + replace(as_string(path), /'/g, "'\\''") + "'");
|
||||
}
|
||||
|
||||
function clean_managed_sing_box(keep_binaries) {
|
||||
if (fs.stat("/etc/init.d/sing-box") != null) {
|
||||
let content = fs.readfile("/etc/init.d/sing-box");
|
||||
if (content && index(content, "Tachyon managed sing-box") >= 0) {
|
||||
run_cmd(["/etc/init.d/sing-box", "stop"]);
|
||||
run_cmd(["/etc/init.d/sing-box", "disable"]);
|
||||
remove_file("/etc/init.d/sing-box");
|
||||
run_shell("rm -f /etc/rc.d/*sing-box* 2>/dev/null || true");
|
||||
}
|
||||
}
|
||||
|
||||
if (!keep_binaries) {
|
||||
if (fs.stat("/bin/opkg") != null || fs.stat("/usr/bin/opkg") != null) {
|
||||
for (let pkg in [ "sing-box-extended", "sing-box-tiny" ]) {
|
||||
let check = system("opkg list-installed '" + pkg + "' 2>/dev/null | grep -q '^" + pkg + " '");
|
||||
if (check == 0)
|
||||
run_cmd(["opkg", "remove", "--force-depends", "--force-remove", pkg]);
|
||||
}
|
||||
if (fs.stat("/etc/init.d/sing-box") == null) {
|
||||
let check_sb = system("opkg list-installed 'sing-box' 2>/dev/null | grep -q '^sing-box '");
|
||||
if (check_sb == 0)
|
||||
run_cmd(["opkg", "remove", "--force-depends", "--force-remove", "sing-box"]);
|
||||
remove_file("/usr/bin/sing-box");
|
||||
}
|
||||
}
|
||||
remove_file("/usr/lib/libcronet.so");
|
||||
}
|
||||
}
|
||||
|
||||
function clean_rt_tables() {
|
||||
let rt_path = "/etc/iproute2/rt_tables";
|
||||
if (fs.stat(rt_path) == null)
|
||||
return;
|
||||
|
||||
let content = fs.readfile(rt_path);
|
||||
if (!content || index(content, "tachyon") < 0)
|
||||
return;
|
||||
|
||||
let lines = split(content, "\n");
|
||||
let clean = [];
|
||||
for (let line in lines) {
|
||||
if (match(line, /105\s+tachyon/) || match(line, /^\s*\d+\s+tachyon\s*$/))
|
||||
continue;
|
||||
push(clean, line);
|
||||
}
|
||||
fs.writefile(rt_path, join("\n", clean) + "\n");
|
||||
}
|
||||
|
||||
function clean_crontab() {
|
||||
run_shell("crontab -l 2>/dev/null | grep -v -E 'tachyon|parental_quota_tick' | crontab - 2>/dev/null || true");
|
||||
}
|
||||
|
||||
function restore_dhcp_dns() {
|
||||
let uci_core = null;
|
||||
try {
|
||||
uci_core = require("core.uci");
|
||||
} catch (e) {}
|
||||
|
||||
if (uci_core != null) {
|
||||
try {
|
||||
// Remove 127.0.0.42 from server list
|
||||
let servers = uci_core.get("dhcp", "@dnsmasq[0]", "server");
|
||||
if (servers != null) {
|
||||
let s_list = type(servers) == "array" ? servers : [ servers ];
|
||||
let clean_servers = [];
|
||||
for (let s in s_list) {
|
||||
s = as_string(s);
|
||||
if (s != "127.0.0.42" && substr(s, 0, 13) != "127.0.0.42#")
|
||||
push(clean_servers, s);
|
||||
}
|
||||
uci_core.delete("dhcp", "@dnsmasq[0]", "server");
|
||||
for (let s in clean_servers)
|
||||
uci_core.add_list("dhcp", "@dnsmasq[0]", "server", s);
|
||||
}
|
||||
|
||||
// Restore original server backup if saved
|
||||
let backup_servers = uci_core.get("dhcp", "@dnsmasq[0]", "tachyon_server");
|
||||
if (backup_servers != null && backup_servers != "") {
|
||||
uci_core.delete("dhcp", "@dnsmasq[0]", "server");
|
||||
let list = type(backup_servers) == "array" ? backup_servers : split(trim(backup_servers), /[ \t\r\n]+/);
|
||||
for (let s in list)
|
||||
if (s != "")
|
||||
uci_core.add_list("dhcp", "@dnsmasq[0]", "server", s);
|
||||
uci_core.delete("dhcp", "@dnsmasq[0]", "tachyon_server");
|
||||
}
|
||||
|
||||
// Restore backed up options
|
||||
for (let opt in [ "noresolv", "cachesize", "rebind_protection", "localuse", "addn_hosts", "notinterface" ]) {
|
||||
let val = uci_core.get("dhcp", "@dnsmasq[0]", "tachyon_" + opt);
|
||||
if (val != null && val != "") {
|
||||
uci_core.set("dhcp", "@dnsmasq[0]", opt, val);
|
||||
uci_core.delete("dhcp", "@dnsmasq[0]", "tachyon_" + opt);
|
||||
}
|
||||
}
|
||||
|
||||
// Failsafe sensible defaults
|
||||
if (uci_core.get("dhcp", "@dnsmasq[0]", "noresolv") == "1")
|
||||
uci_core.set("dhcp", "@dnsmasq[0]", "noresolv", "0");
|
||||
if (uci_core.get("dhcp", "@dnsmasq[0]", "cachesize") == "0")
|
||||
uci_core.set("dhcp", "@dnsmasq[0]", "cachesize", "150");
|
||||
|
||||
uci_core.delete("dhcp", "tachyon");
|
||||
uci_core.commit("dhcp");
|
||||
} catch (e) {}
|
||||
} else {
|
||||
run_shell("uci -q del_list dhcp.@dnsmasq[0].server='127.0.0.42' 2>/dev/null || true");
|
||||
run_shell("uci -q set dhcp.@dnsmasq[0].noresolv='0' 2>/dev/null || true");
|
||||
run_shell("uci -q set dhcp.@dnsmasq[0].cachesize='150' 2>/dev/null || true");
|
||||
run_shell("uci -q delete dhcp.tachyon 2>/dev/null || true");
|
||||
run_shell("uci -q commit dhcp 2>/dev/null || true");
|
||||
}
|
||||
|
||||
remove_file("/etc/dnsmasq.d/tachyon.conf");
|
||||
remove_file("/tmp/dnsmasq.d/tachyon.conf");
|
||||
run_shell("rm -f /etc/dnsmasq.d/tachyon*.conf /tmp/dnsmasq.d/tachyon*.conf 2>/dev/null || true");
|
||||
|
||||
if (fs.stat("/etc/init.d/dnsmasq") != null)
|
||||
run_cmd(["/etc/init.d/dnsmasq", "restart"]);
|
||||
}
|
||||
|
||||
function clean_firewall_and_routing() {
|
||||
run_cmd(["nft", "delete", "table", "inet", "TachyonTable"]);
|
||||
run_cmd(["nft", "delete", "table", "inet", "tachyon"]);
|
||||
run_cmd(["nft", "delete", "table", "ip", "tachyon"]);
|
||||
run_cmd(["nft", "delete", "table", "ip6", "tachyon"]);
|
||||
remove_file("/usr/share/nftables.d/chain-pre/input/10-tachyon.nft");
|
||||
|
||||
// Restart firewall to purge in-memory dynamic rules from inet fw4
|
||||
if (fs.stat("/etc/init.d/firewall") != null)
|
||||
run_cmd(["/etc/init.d/firewall", "restart"]);
|
||||
|
||||
run_cmd(["ip", "-4", "rule", "del", "fwmark", "0x04000000/0x04000000", "table", "tachyon", "priority", "105"]);
|
||||
run_cmd(["ip", "-6", "rule", "del", "fwmark", "0x04000000/0x04000000", "table", "tachyon", "priority", "105"]);
|
||||
run_cmd(["ip", "-4", "rule", "del", "fwmark", "0x10000000/0x10000000", "lookup", "100"]);
|
||||
run_cmd(["ip", "-4", "rule", "del", "fwmark", "0x1/0x1", "lookup", "100"]);
|
||||
run_cmd(["ip", "-4", "rule", "del", "fwmark", "0x2/0x2", "lookup", "100"]);
|
||||
run_cmd(["ip", "route", "flush", "table", "tachyon"]);
|
||||
run_cmd(["ip", "route", "flush", "table", "105"]);
|
||||
run_cmd(["ip", "route", "flush", "table", "100"]);
|
||||
clean_rt_tables();
|
||||
|
||||
// Clean network namespaces
|
||||
run_cmd(["ip", "netns", "del", "fkpsc"]);
|
||||
run_cmd(["ip", "link", "del", "fkpsc0"]);
|
||||
remove_tree("/etc/netns/fkpsc");
|
||||
}
|
||||
|
||||
function uninstall_tachyon(purge_config, keep_binaries) {
|
||||
log_step("Stopping and disabling Tachyon service...");
|
||||
remove_file("/var/run/tachyon/starting");
|
||||
remove_file("/var/run/tachyon/reloading");
|
||||
run_shell("rm -f /var/run/tachyon*.lock 2>/dev/null || true");
|
||||
|
||||
if (fs.stat("/etc/init.d/tachyon") != null) {
|
||||
run_cmd(["/etc/init.d/tachyon", "stop"]);
|
||||
run_cmd(["/etc/init.d/tachyon", "disable"]);
|
||||
}
|
||||
|
||||
clean_managed_sing_box(keep_binaries);
|
||||
|
||||
// Stop proxy and desync processes
|
||||
run_shell("killall sing-box nfqws nfqws2 ciadpi 2>/dev/null || true");
|
||||
// Stop background daemons safely (excluding self PID)
|
||||
run_shell("ps 2>/dev/null | grep -E 'dns_failover|watchdog|telegram' | grep -v grep | awk '{print $1}' | while read _pid; do [ \"$_pid\" != \"$$\" ] && kill -9 \"$_pid\" 2>/dev/null; done; true");
|
||||
|
||||
log_step("Cleaning up firewall, nftables and routing rules...");
|
||||
clean_firewall_and_routing();
|
||||
|
||||
log_step("Restoring DNS and dnsmasq configuration...");
|
||||
restore_dhcp_dns();
|
||||
|
||||
log_step("Cleaning crontab jobs...");
|
||||
clean_crontab();
|
||||
|
||||
log_step("Cleaning auxiliary files and hooks...");
|
||||
remove_file("/etc/hotplug.d/iface/99-tachyon-wan-monitor");
|
||||
remove_file("/www/cgi-bin/tachyon-agent");
|
||||
remove_file("/usr/lib/cgi-bin/tachyon-agent");
|
||||
remove_file("/usr/bin/tachyon");
|
||||
remove_file("/etc/init.d/tachyon");
|
||||
remove_file("/etc/tachyon_commit");
|
||||
remove_file("/etc/uci-defaults/50_luci-tachyon");
|
||||
|
||||
remove_tree("/var/run/tachyon");
|
||||
run_shell("rm -rf /var/run/tachyon* /var/log/tachyon* /tmp/tachyon* /tmp/sing-box /tmp/ai_doctor* /tmp/tg_* /tmp/warp_* 2>/dev/null || true");
|
||||
|
||||
if (purge_config) {
|
||||
log_step("Purging Tachyon configuration and state (--purge)...");
|
||||
run_shell("rm -rf /etc/config/tachyon* /etc/tachyon /etc/.tachyon /etc/backup/tachyon_config /etc/sing-box 2>/dev/null || true");
|
||||
} else {
|
||||
log_step("Backing up Tachyon configuration...");
|
||||
let ts = clock()[0];
|
||||
run_shell("cp -af /etc/config/tachyon /etc/config/tachyon.backup-" + ts + " 2>/dev/null || true");
|
||||
run_shell("cp -af /etc/config/tachyon /etc/config/tachyon.bak 2>/dev/null || true");
|
||||
run_shell("chmod 600 /etc/config/tachyon.backup-* /etc/config/tachyon.bak 2>/dev/null || true");
|
||||
}
|
||||
|
||||
log_step("Removing Tachyon packages...");
|
||||
if (fs.stat("/bin/opkg") != null || fs.stat("/usr/bin/opkg") != null) {
|
||||
run_cmd(["opkg", "remove", "--force-depends", "--force-remove", "luci-i18n-tachyon-ru", "luci-app-tachyon", "tachyon"]);
|
||||
} else if (fs.stat("/sbin/apk") != null || fs.stat("/usr/sbin/apk") != null) {
|
||||
run_cmd(["apk", "del", "luci-i18n-tachyon-ru", "luci-app-tachyon", "tachyon"]);
|
||||
}
|
||||
|
||||
// Clean translations and LuCI views
|
||||
run_shell("rm -rf /usr/lib/tachyon /usr/share/tachyon /www/luci-static/resources/view/tachyon /usr/share/luci/menu.d/luci-app-tachyon.json /usr/share/rpcd/acl.d/luci-app-tachyon.json /usr/lib/lua/luci/i18n/tachyon.* 2>/dev/null || true");
|
||||
|
||||
// Clear LuCI cache and restart rpcd/uhttpd
|
||||
run_shell("rm -f /var/luci-indexcache* /tmp/luci-indexcache* /tmp/luci-modulecache/* 2>/dev/null || true");
|
||||
if (fs.stat("/etc/init.d/rpcd") != null)
|
||||
run_cmd(["/etc/init.d/rpcd", "restart"]);
|
||||
if (fs.stat("/etc/init.d/uhttpd") != null)
|
||||
run_cmd(["/etc/init.d/uhttpd", "restart"]);
|
||||
|
||||
// Restore parent Forkop / Podkop services if present
|
||||
if (fs.stat("/etc/init.d/forkop") != null) {
|
||||
log_step("Forkop detected! Restoring Forkop service...");
|
||||
run_cmd(["/etc/init.d/forkop", "enable"]);
|
||||
run_cmd(["/etc/init.d/forkop", "restart"]);
|
||||
} else if (fs.stat("/etc/init.d/podkop") != null) {
|
||||
log_step("Podkop detected! Restoring Podkop service...");
|
||||
run_cmd(["/etc/init.d/podkop", "enable"]);
|
||||
run_cmd(["/etc/init.d/podkop", "restart"]);
|
||||
}
|
||||
|
||||
log_step("Tachyon has been successfully uninstalled.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
let purge = false;
|
||||
let keep_bin = false;
|
||||
for (let i = 0; i < length(ARGV); i++) {
|
||||
let arg = as_string(ARGV[i]);
|
||||
if (arg == "--purge" || arg == "-p")
|
||||
purge = true;
|
||||
if (arg == "--keep-binaries")
|
||||
keep_bin = true;
|
||||
}
|
||||
|
||||
exit(uninstall_tachyon(purge, keep_bin));
|
||||
Reference in New Issue
Block a user