summaryrefslogtreecommitdiffhomepage
path: root/applications/luci-app-ddns/root/usr/share/rpcd/ucode
diff options
context:
space:
mode:
authorPaul Donald <newtwen+github@gmail.com>2024-11-21 18:34:45 +0100
committerPaul Donald <newtwen+github@gmail.com>2024-11-21 18:34:45 +0100
commit22fcc720ec5d2ad1426509136447172281d345a0 (patch)
treea76947bc0d038701f10f4174bd8f8f90846fe046 /applications/luci-app-ddns/root/usr/share/rpcd/ucode
parent53e36e3293bee2ea2ce0fbc1250074c44cfe8335 (diff)
luci-app-ddns: Convert to JS
Signed-off-by: Paul Donald <newtwen+github@gmail.com>
Diffstat (limited to 'applications/luci-app-ddns/root/usr/share/rpcd/ucode')
-rw-r--r--applications/luci-app-ddns/root/usr/share/rpcd/ucode/ddns.uc330
1 files changed, 330 insertions, 0 deletions
diff --git a/applications/luci-app-ddns/root/usr/share/rpcd/ucode/ddns.uc b/applications/luci-app-ddns/root/usr/share/rpcd/ucode/ddns.uc
new file mode 100644
index 0000000000..3a8e5cf50a
--- /dev/null
+++ b/applications/luci-app-ddns/root/usr/share/rpcd/ucode/ddns.uc
@@ -0,0 +1,330 @@
+#!/usr/bin/env ucode
+
+'use strict';
+
+import { readfile, mkstemp, open, popen, stat, glob } from 'fs';
+import { init_list, init_index, init_enabled, init_action, conntrack_list, process_list } from 'luci.sys';
+import { isnan } from 'math';
+import { cursor } from 'uci';
+
+const uci = cursor();
+const ddns_log_path = '/var/log/ddns';
+const ddns_package_path = '/usr/share/ddns';
+const ddns_run_path = '/var/run/ddns';
+const luci_helper = '/usr/lib/ddns/dynamic_dns_lucihelper.sh';
+const srv_name = 'ddns-scripts';
+const opkg_info_path = '/usr/lib/opkg/info';
+
+
+
+
+function get_dateformat() {
+ return uci.get('ddns', 'global', 'ddns_dateformat') || '%F %R';
+}
+
+function uptime() {
+ return split(readfile('/proc/uptime', 256), ' ')?.[0];
+}
+
+function killcmd(procid, signal) {
+ if (!signal) {
+ signal = 0;
+ }
+ // by default, we simply re-nice a process to check it is running
+ return system(`kill -${signal} ${procid}`);
+}
+
+function trimnonewline(input) {
+ return replace(trim(input), /\n/g, '');
+}
+
+function get_date(seconds, format) {
+ return trimnonewline( popen(`date -d @${seconds} "+${format}" 2>/dev/null`, 'r')?.read?.('line') );
+}
+
+// convert epoch date to given format
+function epoch2date(epoch, format) {
+ if (!format || length(format) < 2) {
+ format = get_dateformat();
+ // uci.unload('ddns'); //don't do this in uci.foreach loops
+ }
+ format = replace(format, /%n/g, '<br />'); // Replace '%n' with '<br />'
+ format = replace(format, /%t/g, ' '); // Replace '%t' with four spaces
+
+ return get_date(epoch, format);
+}
+
+// function to calculate seconds from given interval and unit
+function calc_seconds(interval, unit) {
+ let parsedInterval = int(interval);
+ if (isnan(parsedInterval)) {
+ return null;
+ }
+
+ switch (unit) {
+ case 'days':
+ return parsedInterval * 86400; // 60 sec * 60 min * 24 h
+ case 'hours':
+ return parsedInterval * 3600; // 60 sec * 60 min
+ case 'minutes':
+ return parsedInterval * 60; // 60 sec
+ case 'seconds':
+ return parsedInterval;
+ default:
+ return null;
+ }
+}
+
+const methods = {
+ get_services_log: {
+ args: { service_name: 'service_name' },
+ call: function(request) {
+ let result = 'File not found or empty';
+
+ // Get the log directory. Fall back to '/var/log/ddns' if not found
+ let logdir = uci.get('ddns', 'global', 'ddns_logdir') || ddns_log_path;
+
+ // Fall back to default logdir with insecure path
+ if (match(logdir, /\.\.\//)) {
+ logdir = ddns_log_path;
+ }
+
+ // Check if service_name is provided and log file exists
+ if (request.args && request.args.service_name && stat(`${logdir}/${request.args.service_name}.log`)?.type == 'file' ) {
+ result = readfile(`${logdir}/${request.args.service_name}.log`);
+ }
+
+ uci.unload();
+ return { result: result };
+ }
+ },
+
+ get_services_status: {
+ call: function() {
+ const rundir = uci.get('ddns', 'global', 'ddns_rundir') || ddns_run_path;
+ // const dateFormat = get_dateformat();
+ let res = {};
+
+ uci.foreach('ddns', 'service', function(s) {
+ /* uci.foreach danger zone: if you inadvertently call uci.unload('ddns')
+ anywhere in this foreach loop, you will produce some spectacular undefined behaviour */
+ let ip, lastUpdate, nextUpdate;
+ const section = s['.name'];
+ if (section == '.anonymous')
+ return;
+
+ if (stat(`${rundir}/${section}.ip`)?.type == 'file') {
+ ip = readfile(`${rundir}/${section}.ip`);
+ } else {
+ const dnsServer = s['dns_server'] || '';
+ const forceIpVersion = int(s['force_ipversion'] || 0);
+ const forceDnsTcp = int(s['force_dnstcp'] || 0);
+ const isGlue = int(s['is_glue'] || 0);
+ const useIpv6 = int(s['use_ipv6'] || 0);
+ const lookupHost = s['lookup_host'] || '_nolookup_';
+ let command = [luci_helper];
+
+ if (useIpv6 == 1) push(command, '-6');
+ if (forceIpVersion == 1) push(command, '-f');
+ if (forceDnsTcp == 1) push(command, '-t');
+ if (isGlue == 1) push(command, '-g');
+
+ push(command, '-l', lookupHost);
+ push(command, '-S', section);
+ if (length(dnsServer) > 0) push(command, '-d', dnsServer);
+ push(command, '-- get_registered_ip');
+
+ const result = system(`${join(' ', command)}`);
+ }
+
+ lastUpdate = int(readfile(`${rundir}/${section}.update`) || 0);
+
+ let pid = int(readfile(`${rundir}/${section}.pid`) || 0);
+
+ // if killcmd succeeds (0) to re-nice the process, we do not assume the pid is dead
+ if (pid > 0 && killcmd(pid)) {
+ pid = 0;
+ }
+
+ let _uptime = int(uptime());
+
+ const forceSeconds = calc_seconds(
+ int(s['force_interval']) || 72,
+ s['force_unit'] || 'hours'
+ );
+
+ const checkSeconds = calc_seconds(
+ int(s['check_interval']) || 10,
+ s['check_unit'] || 'minutes'
+ );
+
+ let convertedLastUpdate;
+ if (lastUpdate > 0) {
+ const epoch = time() - _uptime + lastUpdate;
+ convertedLastUpdate = epoch2date(epoch);
+ // convertedLastUpdate = get_date(epoch, dateFormat);
+ nextUpdate = epoch2date(epoch + forceSeconds + checkSeconds);
+ // nextUpdate = get_date(epoch + forceSeconds + checkSeconds, dateFormat);
+ }
+
+ if (pid > 0 && (lastUpdate + forceSeconds + checkSeconds - _uptime) <= 0) {
+ nextUpdate = 'Verify';
+ } else if (forceSeconds === 0) {
+ nextUpdate = 'Run once';
+ } else if (pid == 0 && s['enabled'] == '0') {
+ nextUpdate = 'Disabled';
+ } else if (pid == 0 && s['enabled'] != '0') {
+ nextUpdate = 'Stopped';
+ }
+
+ res[section] = {
+ ip: ip ? replace(ip, '\n', '') : null,
+ last_update: lastUpdate !== 0 ? convertedLastUpdate : null,
+ next_update: nextUpdate || null,
+ pid: pid || null,
+ };
+ });
+
+ uci.unload('ddns');
+ return res;
+ }
+ },
+
+ get_ddns_state: {
+ call: function() {
+ // const dateFormat = get_dateformat();
+
+ const services_mtime = stat(ddns_package_path + '/list')?.mtime;
+ // uci.unload('ddns');
+ let res = {};
+ let ver, control;
+
+ if (stat(opkg_info_path + `/${srv_name}.control`)?.type == 'file') {
+ control = readfile(opkg_info_path + `/${srv_name}.control`);
+ }
+
+ for (let line in split(control, '\n')) {
+ ver = match(line, /^Version: (.+)$/)?.[1];
+ if ( ver && length(ver) > 0 )
+ break;
+ }
+
+ ver = ver || trimnonewline(popen(`${luci_helper} -V | awk {'print $2'}`, 'r')?.read?.('line'));
+
+ res['_version'] = ver;
+ res['_enabled'] = init_enabled('ddns');
+ // res['_curr_dateformat'] = get_date(time(), dateFormat);
+ res['_curr_dateformat'] = epoch2date(time());
+ res['_services_list'] = (services_mtime && epoch2date(services_mtime)) || 'NO_LIST';
+
+ uci.unload('ddns');
+ return res;
+ }
+ },
+
+ get_env: {
+ call: function () {
+ let res = {};
+ let cache = {};
+
+ const hasCommand = (command) => {
+ if (system(`command -v ${command}`) == 0)
+ return true;
+ else
+ return false;
+ };
+
+ const hasWget = () => hasCommand('wget');
+
+ const hasWgetSsl = () => {
+ if (cache['has_wgetssl']) return cache['has_wgetssl'];
+ const result = hasWget() && system(`wget 2>&1 | grep -iqF 'https'`) == 0 ? true: false;
+ cache['has_wgetssl'] = result;
+ return result;
+ };
+
+ const hasCurl = () => {
+ if (cache['has_curl']) return cache['has_curl'];
+ const result = hasCommand('curl');
+ cache['has_curl'] = result;
+ return result;
+ };
+
+ const hasCurlSsl = () => {
+ return system(`curl -V 2>&1 | grep -qF 'https'`) == 0 ? true: false;
+ };
+
+ const hasFetch = () => {
+ if (cache['has_fetch']) return cache['has_fetch'];
+ const result = hasCommand('uclient-fetch');
+ cache['has_fetch'] = result;
+ return result;
+ };
+
+ const hasFetchSsl = () => {
+ return stat('/lib/libustream-ssl.so') == 0 ? true: false;
+ };
+
+ const hasCurlPxy = () => {
+ return system(`grep -i 'all_proxy' /usr/lib/libcurl.so*`) == 0 ? true: false;
+ };
+
+ const hasBbwget = () => {
+ return system(`wget -V 2>&1 | grep -iqF 'busybox'`) == 0 ? true: false;
+ };
+
+
+ res['has_wget'] = hasWget();
+ res['has_curl'] = hasCurl();
+
+ res['has_ssl'] = hasWgetSsl() || hasCurlSsl() || (hasFetch() && hasFetchSsl());
+ res['has_proxy'] = hasWgetSsl() || hasCurlPxy() || hasFetch() || hasBbwget();
+ res['has_forceip'] = hasWgetSsl() || hasCurl() || hasFetch();
+ res['has_bindnet'] = hasCurl() || hasWgetSsl();
+
+ const hasBindHost = () => {
+ if (cache['has_bindhost']) return cache['has_bindhost'];
+ const commands = ['host', 'khost', 'drill'];
+ for (let command in commands) {
+ if (hasCommand(command)) {
+ cache['has_bindhost'] = true;
+ return true;
+ }
+ }
+
+ cache['has_bindhost'] = false;
+ return false;
+ };
+
+ res['has_bindhost'] = cache['has_bindhost'] || hasBindHost();
+
+ const hasHostIp = () => {
+ return hasCommand('hostip');
+ };
+
+ const hasNslookup = () => {
+ return hasCommand('nslookup');
+ };
+
+ res['has_dnsserver'] = cache['has_bindhost'] || hasNslookup() || hasHostIp() || hasBindHost();
+
+ const checkCerts = () => {
+ let present = false;
+ for (let cert in glob('/etc/ssl/certs/*.crt', '/etc/ssl/certs/*.pem')) {
+ if (cert != null)
+ present = true;
+ }
+ return present;
+ };
+
+ res['has_cacerts'] = checkCerts();
+
+ res['has_ipv6'] = (stat('/proc/net/ipv6_route')?.type == 'file' &&
+ (stat('/usr/sbin/ip6tables')?.type == 'file' || stat('/usr/sbin/nft')?.type == 'file'));
+
+ return res;
+ }
+ }
+};
+
+return { 'luci.ddns': methods };