#!/usr/bin/env ucode let fs = require("fs"); let uci_core = require("core.uci"); function as_string(value) { return value == null ? "" : "" + value; } function env(name, fallback) { let value = getenv(name); return value == null ? as_string(fallback) : as_string(value); } const CONFIG_NAME = env("TACHYON_CONFIG_NAME", "tachyon"); const RT_TABLES_PATH = env("TACHYON_RT_TABLES", "/etc/iproute2/rt_tables"); const BIN_PATH = env("TACHYON_BIN", "/usr/bin/tachyon"); const INIT_PATH = env("TACHYON_INIT", "/etc/init.d/tachyon"); const DNS_APPLY_UC = env("TACHYON_DNS_APPLY_UC", "/usr/lib/tachyon/dns/apply.uc"); const SING_BOX_INIT = env("TACHYON_SING_BOX_INIT", "/etc/init.d/sing-box"); const SING_BOX_BIN = env("TACHYON_SING_BOX_BIN", "/usr/bin/sing-box"); const SING_BOX_CRONET = env("TACHYON_SING_BOX_CRONET", "/usr/lib/libcronet.so"); const SING_BOX_MANAGED_MARKER = env("SB_MANAGED_SERVICE_MARKER", "Tachyon managed sing-box service for binary variants"); const PACKAGE_UPGRADE_STATE = env("TACHYON_PACKAGE_UPGRADE_STATE", "/tmp/tachyon-package-was-running"); const PACKAGE_TEST_MODE = env("TACHYON_PACKAGE_TEST_MODE", "") != ""; function shell_quote(value) { return "'" + replace(as_string(value), /'/g, "'\\''") + "'"; } function command_from_args(args) { let parts = []; for (let arg in args) push(parts, shell_quote(arg)); return join(" ", parts); } function normalize_status(status) { status = int(status); return status > 255 ? int(status / 256) : status; } function command_success_from_args(args) { return normalize_status(system(command_from_args(args) + " >/dev/null 2>&1")) == 0; } function path_exists(path) { return fs.stat(as_string(path)) != null; } function unlink_if_exists(path) { if (path_exists(path)) fs.unlink(as_string(path)); } function remove_rt_tables_entry() { let data = fs.readfile(RT_TABLES_PATH); if (data == null) return true; let changed = false; let lines = []; for (let line in split(data, "\n")) { if (index(line, "105 tachyon") >= 0) { changed = true; continue; } push(lines, line); } return !changed || fs.writefile(RT_TABLES_PATH, join("\n", lines)) != null; } function ascii_lower(value) { let upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; let lower = "abcdefghijklmnopqrstuvwxyz"; return replace(as_string(value), /[A-Z]/g, function(ch) { return substr(lower, index(upper, ch), 1); }); } function truthy(value) { value = ascii_lower(trim(as_string(value))); return value == "1" || value == "true" || value == "yes" || value == "on"; } function dont_touch_dhcp_enabled() { return truthy(uci_core.get(CONFIG_NAME + ".settings.dont_touch_dhcp")); } function restore_dnsmasq_if_needed() { if (dont_touch_dhcp_enabled()) return; command_success_from_args([ BIN_PATH, "restore_dnsmasq" ]); if (path_exists(DNS_APPLY_UC)) command_success_from_args([ "ucode", DNS_APPLY_UC, "failsafe-restore" ]); } function remove_managed_sing_box() { let data = fs.readfile(SING_BOX_INIT); if (data == null || index(data, SING_BOX_MANAGED_MARKER) < 0) return; command_success_from_args([ SING_BOX_INIT, "stop" ]); command_success_from_args([ SING_BOX_INIT, "disable" ]); unlink_if_exists(SING_BOX_INIT); unlink_if_exists(SING_BOX_BIN); unlink_if_exists(SING_BOX_CRONET); } function remember_upgrade_state(action) { if (as_string(action) != "upgrade") { unlink_if_exists(PACKAGE_UPGRADE_STATE); return; } if (command_success_from_args([ INIT_PATH, "status" ])) fs.writefile(PACKAGE_UPGRADE_STATE, "1\n"); } function prerm_cleanup(action) { if (env("IPKG_INSTROOT", "") != "") return true; remember_upgrade_state(action); if (!PACKAGE_TEST_MODE) { command_success_from_args([ INIT_PATH, "stop" ]); restore_dnsmasq_if_needed(); remove_managed_sing_box(); } return remove_rt_tables_entry(); } function postinst_restore() { if (env("IPKG_INSTROOT", "") != "" || !path_exists(PACKAGE_UPGRADE_STATE)) return true; if (!command_success_from_args([ INIT_PATH, "start" ])) return false; unlink_if_exists(PACKAGE_UPGRADE_STATE); return true; } function luci_cache_globs() { let configured = env("TACHYON_LUCI_CACHE_GLOBS", ""); if (configured != "") return split(configured, /[ \t\r\n]+/); return [ "/var/luci-indexcache*", "/tmp/luci-indexcache*" ]; } function remove_luci_index_cache() { for (let pattern in luci_cache_globs()) { pattern = as_string(pattern); if (pattern == "") continue; for (let path in fs.glob(pattern)) unlink_if_exists(path); } } function luci_postinst() { remove_luci_index_cache(); if (!PACKAGE_TEST_MODE) { if (path_exists("/etc/init.d/rpcd")) command_success_from_args([ "/etc/init.d/rpcd", "reload" ]); command_success_from_args([ "logger", "-t", "tachyon", "[info] Package defaults applied" ]); } return true; } let mode = ARGV[0] || ""; if (mode == "prerm") exit(prerm_cleanup(ARGV[1]) ? 0 : 1); else if (mode == "postinst") exit(postinst_restore() ? 0 : 1); else if (mode == "remove-rt-tables-entry") exit(remove_rt_tables_entry() ? 0 : 1); else if (mode == "luci-postinst") exit(luci_postinst() ? 0 : 1); else { warn("Usage: service/package.uc \n"); exit(1); }