Files
NeoTachyon/tachyon/files/usr/lib/providers/olcrtc/runtime.uc
T

380 lines
12 KiB
Ucode

#!/usr/bin/env ucode
let fs = require("fs");
let common = require("core.common");
let uci_core = require("core.uci");
let connections = require("config.connections");
let as_string = common.as_string;
let bool_value = common.bool_value;
let object_or_empty = common.object_or_empty;
const MODE = as_string(getenv("TACHYON_CONFIG_NAME")) || "tachyon";
const LIB_DIR = as_string(getenv("TACHYON_LIB_DIR")) || "/usr/lib/tachyon";
const CONFIG_NAME = MODE;
let cfg = require("providers.olcrtc.common").config({ lib_dir: LIB_DIR });
let olcrtc_validator = require("providers.olcrtc.validator");
function log_message(msg, level) {
level = level || "info";
printf("%s\n", msg);
system("logger -t " + MODE + " -p " + (level == "fatal" ? "user.err" : level == "warn" ? "user.warning" : "user.info") + " '" + msg + "'");
}
let command_from_args = common.command_from_args;
let command_output = common.command_output;
let command_output_from_args = common.command_output_from_args;
let command_success_from_args = common.command_success_from_args;
let option = common.option;
let bool_option = common.bool_option;
let list_option = common.list_option;
let section_name = common.section_name;
let read_json_file = common.read_json_file;
function yaml_quote(value) {
value = as_string(value);
if (value == "")
return "''";
if (match(value, /^[a-zA-Z0-9_./:-]+$/) != null)
return value;
return "'" + replace(value, "'", "''") + "'";
}
function enabled_sections() {
return connections.olcrtc_sections();
}
function enabled_rule_count() {
let sections = enabled_sections();
return length(sections);
}
function provider_available() {
return fs.stat(cfg.binary) != null;
}
function service_init_exists() {
return fs.stat(cfg.service_init) != null;
}
function service_running() {
return command_success_from_args([cfg.service_init, "status"]);
}
function service_enabled() {
return command_success_from_args([cfg.service_init, "enabled"]);
}
function package_version() {
let ver = trim(fs.readfile("/etc/olcrtc/.version") || "");
if (ver != "")
return ver;
return "";
}
function fetch_url(url) {
let tmp = "/tmp/tachyon-olcrtc-sub";
if (command_success_from_args(["curl", "-fsSL", "-o", tmp, "--connect-timeout", "20", "--max-time", "20", url]))
return trim(fs.readfile(tmp) || "");
if (command_success_from_args(["wget", "-q", "-O", tmp, "--timeout=20", url]))
return trim(fs.readfile(tmp) || "");
if (command_success_from_args(["uclient-fetch", "-q", "-O", tmp, "--timeout=20", url]))
return trim(fs.readfile(tmp) || "");
return "";
}
function first_uri_from_subscription(data) {
let lines = split(data, "\n\r");
for (let line in lines) {
line = trim(line);
if (substr(line, 0, 9) == "olcrtc://")
return line;
}
return "";
}
function resolve_connection(section) {
let links = list_option(section, "subscription_links");
let resolved = null;
let used_link = "";
let subscription_name = "";
let subscription_refresh = "";
for (let link in links) {
link = trim(as_string(link));
if (link == "")
continue;
if (substr(link, 0, 9) == "olcrtc://") {
let parsed = olcrtc_validator.parse_uri(link);
if (parsed.valid) {
resolved = parsed;
used_link = link;
break;
}
log_message("Invalid olcrtc:// URI: " + parsed.reason, "warn");
continue;
}
if (substr(link, 0, 8) == "https://" || substr(link, 0, 7) == "http://") {
let data = fetch_url(link);
if (data == "") {
log_message("Failed to fetch subscription: " + link, "warn");
continue;
}
let uri = first_uri_from_subscription(data);
if (uri == "") {
log_message("No olcrtc:// URI found in subscription: " + link, "warn");
continue;
}
let parsed = olcrtc_validator.parse_uri(uri);
if (parsed.valid) {
resolved = parsed;
used_link = link;
let lines = split(data, "\n\r");
for (let line in lines) {
line = trim(line);
if (substr(line, 0, 6) == "#name:")
subscription_name = trim(substr(line, 6));
else if (substr(line, 0, 8) == "#refresh:")
subscription_refresh = trim(substr(line, 8));
}
break;
}
log_message("Invalid olcrtc URI in subscription: " + parsed.reason, "warn");
}
}
if (resolved == null)
return { valid: false, reason: "No valid connection found in subscription links" };
let provider = option(section, "provider", "");
if (provider == "")
provider = resolved.provider;
let transport = option(section, "transport", "");
if (transport == "")
transport = resolved.transport;
let room_id = option(section, "room_id", "");
if (room_id == "")
room_id = resolved.room_id;
let crypto_key = option(section, "crypto_key", "");
if (crypto_key == "")
crypto_key = resolved.crypto_key;
return {
valid: true,
provider: provider,
transport: transport,
room_id: room_id,
crypto_key: crypto_key,
mimo: resolved.mimo,
payload: resolved.payload,
used_link: used_link,
subscription_name: subscription_name,
subscription_refresh: subscription_refresh
};
}
function payload_option_mapping() {
return {
"vp8-fps": "vp8_fps",
"vp8-batch": "vp8_batch",
"fps": "sei_fps",
"batch": "sei_batch",
"video-w": "video_w",
"video-h": "video_h",
"video-fps": "video_fps",
"video-codec": "video_codec",
"video-qr-size": "video_qr_size",
"video-qr-recovery": "video_qr_recovery",
"video-tile-module": "video_tile_module",
"video-tile-rs": "video_tile_rs"
};
}
function payload_uci_commands(payload) {
let cmds = [];
if (payload == "" || payload == null)
return cmds;
let mapping = payload_option_mapping();
let pairs = split(payload, "&");
for (let pair in pairs) {
let kv = split(pair, "=", 2);
if (length(kv) == 2) {
let key = kv[0];
let val = kv[1];
if (mapping[key] != null)
push(cmds, "uci set olcrtc.config." + mapping[key] + "=" + yaml_quote(val));
}
}
return cmds;
}
function write_olcrtc_config(section, connection) {
let uci_cmds = [
"uci set olcrtc.config.carrier=" + yaml_quote(connection.provider),
"uci set olcrtc.config.transport=" + yaml_quote(connection.transport),
"uci set olcrtc.config.room_id=" + yaml_quote(connection.room_id),
"uci set olcrtc.config.key=" + yaml_quote(connection.crypto_key),
"uci set olcrtc.config.socks_host=" + yaml_quote(option(section, "socks_host", cfg.default_socks_host)),
"uci set olcrtc.config.socks_port=" + yaml_quote(option(section, "socks_port", cfg.default_socks_port)),
"uci set olcrtc.config.dns=" + yaml_quote(option(section, "dns_server", cfg.default_dns))
];
let socks_user = option(section, "socks_user", "");
if (socks_user != "")
push(uci_cmds, "uci set olcrtc.config.socks_user=" + yaml_quote(socks_user));
let socks_pass = option(section, "socks_pass", "");
if (socks_pass != "")
push(uci_cmds, "uci set olcrtc.config.socks_pass=" + yaml_quote(socks_pass));
let payload_cmds = payload_uci_commands(connection.payload);
for (let cmd in payload_cmds)
push(uci_cmds, cmd);
for (let cmd in uci_cmds)
system(cmd);
system("uci commit olcrtc");
return true;
}
function write_olcrtc_yaml_config(section, connection) {
let yaml = "mode: cnc\n";
yaml += "auth:\n";
yaml += " provider: " + yaml_quote(connection.provider) + "\n";
yaml += "room:\n";
yaml += " id: " + yaml_quote(connection.room_id) + "\n";
yaml += "crypto:\n";
yaml += " key: " + yaml_quote(connection.crypto_key) + "\n";
yaml += "net:\n";
yaml += " transport: " + yaml_quote(connection.transport) + "\n";
yaml += " dns: " + yaml_quote(option(section, "dns_server", cfg.default_dns)) + "\n";
yaml += "socks:\n";
yaml += " host: " + yaml_quote(option(section, "socks_host", cfg.default_socks_host)) + "\n";
yaml += " port: " + yaml_quote(option(section, "socks_port", cfg.default_socks_port)) + "\n";
let socks_user = option(section, "socks_user", "");
if (socks_user != "") {
yaml += " username: " + yaml_quote(socks_user) + "\n";
yaml += " password: " + yaml_quote(option(section, "socks_pass", "")) + "\n";
}
let tmp = cfg.config_path + ".tmp";
let f = fs.open(tmp, "w");
if (!f)
return false;
f.write(yaml);
f.close();
return rename(tmp, cfg.config_path) == 0;
}
function start_runtime() {
let sections = enabled_sections();
if (length(sections) == 0) {
log_message("No enabled OlcRTC sections found, stopping service");
command_success_from_args([cfg.service_init, "stop"]);
return true;
}
if (!provider_available()) {
log_message("OlcRTC provider not available (binary not found: " + cfg.binary + ")", "fatal");
return false;
}
if (!service_init_exists()) {
log_message("OlcRTC service init script not found: " + cfg.service_init, "fatal");
return false;
}
if (length(sections) > 1)
log_message("Multiple OlcRTC sections found, using first: " + section_name(sections[0]), "warn");
let section = sections[0];
let connection = resolve_connection(section);
if (!connection.valid) {
log_message("OlcRTC rule '" + section_name(section) + "': " + connection.reason, "fatal");
return false;
}
let config_type = fs.stat("/etc/olcrtc/client.yaml") != null ? "yaml" : "uci";
if (config_type == "yaml") {
if (!write_olcrtc_yaml_config(section, connection)) {
log_message("Failed to write OlcRTC YAML config", "fatal");
return false;
}
}
else {
if (!write_olcrtc_config(section, connection)) {
log_message("Failed to write OlcRTC UCI config", "fatal");
return false;
}
}
if (!command_success_from_args([cfg.service_init, "restart"])) {
log_message("Failed to restart OlcRTC service", "fatal");
return false;
}
log_message("OlcRTC provider started successfully (provider=" + connection.provider + ", transport=" + connection.transport + ")");
return true;
}
function stop_runtime() {
command_success_from_args([cfg.service_init, "stop"]);
log_message("OlcRTC provider stopped");
return true;
}
function status_json() {
let installed = provider_available();
let running = installed ? service_running() : false;
let enabled = installed ? service_enabled() : false;
let rule_count = enabled_rule_count();
let version = package_version();
write_json({
installed: installed,
configured: rule_count > 0,
enabled_rule_count: rule_count,
service_enabled: enabled,
service_running: running,
config_path: cfg.config_path,
binary: cfg.binary,
service_init: cfg.service_init,
ready: installed && running && rule_count > 0,
status_message: running ? "olcrtc provider status is normal" : (installed ? "olcrtc service is not running" : "olcrtc is not installed")
});
return true;
}
function check_json() {
write_json({
olcrtc_installed: provider_available(),
olcrtc_config_path: cfg.config_path
});
return true;
}
let mode = as_string(ARGV[0]);
if (mode == "start-runtime")
start_runtime();
else if (mode == "stop-runtime")
stop_runtime();
else if (mode == "status")
status_json();
else if (mode == "check")
check_json();
else if (mode == "installed" || mode == "provider-available")
exit(provider_available() ? 0 : 1);
else if (mode == "package-version")
printf("%s\n", package_version());
else if (mode == "enabled-rule-count")
printf("%d\n", enabled_rule_count());
else {
warn("Usage: olcrtc/runtime.uc <operation>\n");
exit(1);
}