diff --git a/emhttp/plugins/dynamix/DateTime.page b/emhttp/plugins/dynamix/DateTime.page
index 3c1c13f5a..212de12b3 100644
--- a/emhttp/plugins/dynamix/DateTime.page
+++ b/emhttp/plugins/dynamix/DateTime.page
@@ -4,8 +4,8 @@ Icon="icon-clock"
Tag="clock-o"
---
-$keys = explode("\n",file_get_contents("$docroot/webGui/include/timezones.key"));
+$validIP4 = "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$";
+$validIP6 = "^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(:|([0-9a-fA-F]{1,4}:)+):(([0-9a-fA-F]{1,4}:)*[0-9a-fA-F]{1,4})?)$";
+$validMAC = "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$";
+$tmzones = file("$docroot/webGui/include/timezones.key",FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
+$hwclock = [];
+
+exec("ls --indicator-style=none /sys/class/net|grep -P '^eth[0-9]*$'",$ports);
+foreach ($ports as $port) $hwclock[$port] = exec("ethtool -T $port|grep -Pom1 '^PTP Hardware Clock: \K.+'")=='none' ? 'disabled' : '';
?>
-
diff --git a/emhttp/plugins/dynamix/include/DashboardApps.php b/emhttp/plugins/dynamix/include/DashboardApps.php
index 871d29abb..d2ac16a03 100644
--- a/emhttp/plugins/dynamix/include/DashboardApps.php
+++ b/emhttp/plugins/dynamix/include/DashboardApps.php
@@ -1,6 +1,6 @@
0) {
$wsport = $lv->domain_get_ws_port($res);
- $vmrcprotocol = $lv->domain_get_vmrc_protocol($res) ;
+ $vmrcprotocol = $lv->domain_get_vmrc_protocol($res);
if ($vmrcprotocol == "vnc") $vmrcscale = "&resize=scale"; else $vmrcscale = "";
- $vmrcurl = autov('/plugins/dynamix.vm.manager/'.$vmrcprotocol.'.html',true).$vmrcscale.'&autoconnect=true&host=' . $_SERVER['HTTP_HOST'] ;
- if ($vmrcprotocol == "spice") $vmrcurl .= '&vmname='. urlencode($vm) . '&port=/wsproxy/'.$vmrcport.'/' ; else $vmrcurl .= '&port=&path=/wsproxy/' . $wsport . '/';
+ $vmrcurl = autov('/plugins/dynamix.vm.manager/'.$vmrcprotocol.'.html',true).$vmrcscale.'&autoconnect=true&host=' . $_SERVER['HTTP_HOST'];
+ if ($vmrcprotocol == "spice") $vmrcurl .= '&vmname='. urlencode($vm) . '&port=/wsproxy/'.$vmrcport.'/'; else $vmrcurl .= '&port=&path=/wsproxy/' . $wsport . '/';
} elseif ($vmrcport == -1 || $autoport) {
- $vmrcprotocol = $lv->domain_get_vmrc_protocol($res) ;
- if ($autoport == "yes") $auto = "auto" ; else $auto="manual" ;
+ $vmrcprotocol = $lv->domain_get_vmrc_protocol($res);
+ $auto = ($autoport == "yes") ? "auto" : "manual";
} elseif (!empty($arrConfig['gpu'])) {
$arrValidGPUDevices = getValidGPUDevices();
foreach ($arrConfig['gpu'] as $arrGPU) {
foreach ($arrValidGPUDevices as $arrDev) {
if ($arrGPU['id'] == $arrDev['id']) {
if (count(array_filter($arrValidGPUDevices, function($v) use ($arrDev) { return $v['name'] == $arrDev['name']; })) > 1) {
- $vmrcprotocol = "VGA" ;
+ $vmrcprotocol = "VGA";
} else {
- $vmrcprotocol = "VGA" ;
+ $vmrcprotocol = "VGA";
}
}
}
}
- }
+ }
$template = $lv->_get_single_xpath_result($res, '//domain/metadata/*[local-name()=\'vmtemplate\']/@name');
if (empty($template)) $template = 'Custom';
$log = (is_file("/var/log/libvirt/qemu/$vm.log") ? "libvirt/qemu/$vm.log" : '');
- if (!isset($domain_cfg["CONSOLE"])) $vmrcconsole = "web" ; else $vmrcconsole = $domain_cfg["CONSOLE"] ;
- if (!isset($domain_cfg["RDPOPT"])) $vmrcconsole .= ";no" ; else $vmrcconsole .= ";".$domain_cfg["RDPOPT"] ;
+ if (!isset($domain_cfg["CONSOLE"])) $vmrcconsole = "web"; else $vmrcconsole = $domain_cfg["CONSOLE"];
+ if (!isset($domain_cfg["RDPOPT"])) $vmrcconsole .= ";no"; else $vmrcconsole .= ";".$domain_cfg["RDPOPT"];
$WebUI = html_entity_decode($arrConfig["template"]["webui"]);
$menu = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')\"", addslashes($vm), addslashes($uuid), addslashes($template), $state, addslashes($vmrcurl), strtoupper($vmrcprotocol), addslashes($log),addslashes($fstype), $vmrcconsole,false,addslashes(str_replace('"',"'",$WebUI)));
$icon = $lv->domain_get_icon_url($res);
@@ -153,7 +181,7 @@ if ($_POST['vms']) {
#Build VM Usage array.
$menuusage = sprintf("onclick=\"addVMContext('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')\"", addslashes($vm), addslashes($uuid), addslashes($template), $state, addslashes($vmrcurl), strtoupper($vmrcprotocol), addslashes($log),addslashes($fstype), $vmrcconsole,true,addslashes(str_replace('"',"'",$WebUI)));
$vmusagehtml[] = "$image$vm
"._($status)."";
- $vmusagehtml[] = "
"._("Loading")."....";
+ $vmusagehtml[] = "
"._("Loading")."....";
$vmusagehtml[] = "
"._("Loading")."....";
$vmusagehtml[] = "
"._("Loading")."....";
$vmusagehtml[] = "
"._("Loading")."....";
diff --git a/emhttp/plugins/dynamix/include/StartStopPTP.php b/emhttp/plugins/dynamix/include/StartStopPTP.php
new file mode 100644
index 000000000..553a8a0fe
--- /dev/null
+++ b/emhttp/plugins/dynamix/include/StartStopPTP.php
@@ -0,0 +1,4 @@
+
diff --git a/emhttp/plugins/dynamix/sheets/DateTime.css b/emhttp/plugins/dynamix/sheets/DateTime.css
index b2eb5cbbe..b3012f111 100644
--- a/emhttp/plugins/dynamix/sheets/DateTime.css
+++ b/emhttp/plugins/dynamix/sheets/DateTime.css
@@ -1 +1,3 @@
-span.ntp{margin-left:40px}
+div.extra,span.ipv4,span.ipv6,span.mac{display:none}
+select,input[type=text]{margin-right:40px}
+select[name=timeZone]{max-width:166px}
diff --git a/etc/rc.d/rc.M b/etc/rc.d/rc.M
index 2ae1f5dc8..632ca2e8a 100755
--- a/etc/rc.d/rc.M
+++ b/etc/rc.d/rc.M
@@ -11,7 +11,7 @@
# Heavily modified by Patrick Volkerding
#
# LimeTech - modified for Unraid OS
-# Bergware - modified for Unraid OS, October 2023
+# Bergware - modified for Unraid OS, December 2024
# run & log functions
. /etc/rc.d/rc.runlog
@@ -108,6 +108,11 @@ fi
# Mount any additional filesystem types that haven't already been mounted:
mount -a -v 2>/dev/null | grep -v -e "already mounted" -e "ignored" | cut -f 1 -d : | tr -d ' ' | while read DEV; do mount | grep "$DEV "; done
+# Start the Precision Time Protocol daemon:
+if [[ -x /etc/rc.d/rc.ptpd ]]; then
+ /etc/rc.d/rc.ptpd start
+fi
+
# Start the Network Time Protocol daemon:
if [[ -x /etc/rc.d/rc.ntpd ]]; then
/etc/rc.d/rc.ntpd start
diff --git a/etc/rc.d/rc.library.source b/etc/rc.d/rc.library.source
index a37043f06..3dc4bfdbf 100644
--- a/etc/rc.d/rc.library.source
+++ b/etc/rc.d/rc.library.source
@@ -4,12 +4,20 @@
#
# Library used by nfsd, ntpd, rpc, samba, nginx, sshd, avahidaemon, show_interfaces
#
-# Bergware - created for Unraid OS, December 2023
+# Bergware - created for Unraid OS, December 2024
WIREGUARD="/etc/wireguard"
NETWORK_INI="/var/local/emhttp/network.ini"
NETWORK_EXTRA="/boot/config/network-extra.cfg"
+var(){
+ if [[ $# -eq 3 ]]; then
+ [[ -r "$3" ]] && sed -n "/^\[$1\]\$/,/^\[/p" "$3" | grep -Pom1 "^$2=\"\K[^\"]+"
+ elif [[ $# -eq 2 ]]; then
+ [[ -r "$2" ]] && grep -Pom1 "^$1=\"\K[^\"]+" "$2"
+ fi
+}
+
ipv(){
local t=${1//[^:]}
[[ ${#t} -le 1 ]] && echo 4 || echo 6
diff --git a/etc/rc.d/rc.ntpd b/etc/rc.d/rc.ntpd
index deedfedc9..532c4a033 100755
--- a/etc/rc.d/rc.ntpd
+++ b/etc/rc.d/rc.ntpd
@@ -5,15 +5,15 @@
# Start/stop/restart ntpd.
#
# LimeTech - modified to initialize ntp.conf file from config
-# Bergware - modified for Unraid OS, October 2023
+# Bergware - modified for Unraid OS, December 2024
DAEMON="NTP server daemon"
CALLER="ntp"
NTPD="/usr/sbin/ntpd"
OPTIONS="-g -u ntp:ntp"
CONF="/etc/ntp.conf"
+CFG="/boot/config/plugins/dynamix/dynamix.cfg"
IDENT="/boot/config/ident.cfg"
-CONFIG="/boot/config/plugins/dynamix/dynamix.cfg"
# run & log functions
. /etc/rc.d/rc.runlog
@@ -39,24 +39,25 @@ ntpd_build(){
echo "interface listen $NET" >>$CONF
done
fi
+ NTP_POLL=$(var NTP POLL $CFG)
# ntp poll interval may be adjusted to predefined values
- if [[ -f $CONFIG ]]; then
- NTP_POLL=$(grep -Po '^ntppoll="\K[^"]+' $CONFIG)
- if [[ -n $NTP_POLL ]]; then
- MINPOLL="minpoll $NTP_POLL"
- MAXPOLL="maxpoll $NTP_POLL"
- fi
+ if [[ -n $NTP_POLL ]]; then
+ MINPOLL="minpoll $NTP_POLL"
+ MAXPOLL="maxpoll $NTP_POLL"
+ fi
+ # allow ntp to use ptp as sync source
+ if [[ $(var PTP SYNC $CFG) != yes ]]; then
+ # add configured ntp servers or pools
+ for n in {1..4}; do
+ NTP="NTP_SERVER$n"
+ if [[ -n ${!NTP} ]]; then
+ # use either server or pool peers depending on remote ntp name
+ # pools use a round-robin mechanism to get a server out of the pool
+ [[ ${!NTP} =~ "pool" ]] && PEER=pool || PEER=server
+ echo "$PEER ${!NTP} iburst $MINPOLL $MAXPOLL" >>$CONF
+ fi
+ done
fi
- # add configured ntp servers or pools
- for n in {1..4}; do
- NTP="NTP_SERVER$n"
- if [[ -n ${!NTP} ]]; then
- # use either server or pool peers depending on remote ntp name
- # pools use a round-robin mechanism to get a server out of the pool
- [[ ${!NTP} =~ "pool" ]] && PEER=pool || PEER=server
- echo "$PEER ${!NTP} iburst $MINPOLL $MAXPOLL" >>$CONF
- fi
- done
}
ntpd_start(){
@@ -64,7 +65,7 @@ ntpd_start(){
local REPLY
# read Unraid settings
[[ -r $IDENT ]] && . <(fromdos <$IDENT)
- # if ntp not enabled, don't start ntp
+ # if time sync not enabled, don't start ntp
if [[ $USE_NTP != yes ]]; then
REPLY="Service not enabled"
elif ntpd_running; then
@@ -90,7 +91,7 @@ ntpd_stop(){
kill -HUP $(cat /var/run/ntpd.pid)
rm -f /var/run/ntpd.pid
else
- killall --ns $$ -HUP -q ntpd
+ killall --ns $$ -HUP -q ntpd
fi
if ! ntpd_running; then REPLY="Stopped"; else REPLY="Failed"; fi
fi
@@ -107,7 +108,7 @@ ntpd_restart(){
}
ntpd_reload(){
- killall --ns $$ -HUP -q ntpd
+ killall --ns $$ -HUP -q ntpd
. <(fromdos <$IDENT)
ntpd_build
$NTPD $OPTIONS 2>/dev/null
diff --git a/etc/rc.d/rc.ptpd b/etc/rc.d/rc.ptpd
new file mode 100644
index 000000000..692d7529d
--- /dev/null
+++ b/etc/rc.d/rc.ptpd
@@ -0,0 +1,130 @@
+#!/bin/bash
+#
+# script: rc.ptpd
+#
+# Start/stop/restart services ptp4l and phc2sys.
+#
+# Bergware - created for Unraid OS, December 2024
+
+DAEMON="PTP server daemon"
+CALLER="ptp"
+PTPD="/usr/sbin/ptp4l"
+PHC="/usr/sbin/phc2sys"
+OPTIONS1="-s -l 5 -f /etc/ptp4l.conf"
+OPTIONS2="-a -r -l 5"
+CONF="/etc/ptp4l.conf"
+CFG="/boot/config/plugins/dynamix/dynamix.cfg"
+IDENT="/boot/config/ident.cfg"
+
+# run & log functions
+. /etc/rc.d/rc.runlog
+
+# library functions
+. /etc/rc.d/rc.library.source
+
+ptpd_running(){
+ sleep 0.1
+ [[ $(pgrep -cf $PTPD) -gt 0 ]]
+}
+
+ptpd_build(){
+ echo "[global]" >$CONF
+ TRANSPORT=$(var PTP TRANSPORT $CFG)
+ echo "network_transport $TRANSPORT" >>$CONF
+ echo "time_stamping $(var PTP CLOCK $CFG)" >>$CONF
+ [[ $(var PTP MODE $CFG) == unicast ]] && UNICAST=1 || UNICAST=0
+ if [[ $UNICAST == 1 ]]; then
+ echo "unicast_req_duration 60" >>$CONF
+ echo "" >>$CONF
+ echo "[unicast_master_table]" >>$CONF
+ echo "table_id 1" >>$CONF
+ echo "logQueryInterval 1" >>$CONF
+ for n in {1..4}; do
+ PTP=$(var PTP "SERVER$n" $CFG)
+ [[ -n $PTP ]] && echo "$TRANSPORT $PTP" >>$CONF
+ done
+ fi
+ echo "" >>$CONF
+ echo "[$(var PTP PORT $CFG)]" >>$CONF
+ [[ $UNICAST == 1 ]] && echo "unicast_master_table 1" >>$CONF
+}
+
+ptpd_start(){
+ log "Starting $DAEMON..."
+ local REPLY
+ # read Unraid settings
+ [[ -r $IDENT ]] && . <(fromdos <$IDENT)
+ # if time sync not enabled, don't start ptp
+ if [[ $USE_NTP != yes ]]; then
+ REPLY="Service not enabled"
+ elif [[ $(var PTP SYNC $CFG) == yes || $FORCE == yes ]]; then
+ if ptpd_running; then
+ REPLY="Already started"
+ else
+ # generate our config file
+ ptpd_build
+ $PTPD $OPTIONS1 &>/dev/null &
+ if ptpd_running; then
+ $PHC $OPTIONS2 &>/dev/null &
+ REPLY="Started"
+ else
+ REPLY="Failed"
+ fi
+ fi
+ else
+ REPLY="Not started"
+ fi
+ log "$DAEMON... $REPLY."
+}
+
+ptpd_stop(){
+ log "Stopping $DAEMON..."
+ local REPLY
+ if ! ptpd_running; then
+ REPLY="Already stopped"
+ else
+ pkill -f $PTPD 2>/dev/null
+ pkill -f $PHC 2>/dev/null
+ if ! ptpd_running; then REPLY="Stopped"; else REPLY="Failed"; fi
+ fi
+ log "$DAEMON... $REPLY."
+}
+
+ptpd_restart(){
+ log "Restarting $DAEMON..."
+ ptpd_stop
+ sleep 1
+ ptpd_start
+}
+
+ptpd_status(){
+ if ptpd_running; then
+ echo "$DAEMON is currently running."
+ else
+ echo "$DAEMON is not running."
+ exit 1
+ fi
+}
+
+case "$1" in
+'start')
+ ptpd_start
+ ;;
+'forcestart')
+ FORCE=yes
+ ptpd_start
+ ;;
+'stop')
+ ptpd_stop
+ ;;
+'restart')
+ ptpd_restart
+ ;;
+'status')
+ ptpd_status
+ ;;
+*)
+ echo "Usage: $BASENAME start|forcestart|stop|restart|status"
+ exit 1
+esac
+exit 0