Skip to content

Date and Time enhancements and other fixes#1949

Merged
limetech merged 132 commits into
unraid:masterfrom
bergware:master
Feb 6, 2025
Merged

Date and Time enhancements and other fixes#1949
limetech merged 132 commits into
unraid:masterfrom
bergware:master

Conversation

@bergware

@bergware bergware commented Dec 14, 2024

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Introduced a dedicated Wireless Management interface for scanning, joining, and managing Wi-Fi networks.
    • Added support for Precision Time Protocol (PTP) to enhance time synchronization alongside NTP.
    • Network settings now feature a tabbed layout, providing more organized navigation.
    • Wireless connection status is displayed in real time.
    • Enhanced monitoring for the wlan0 network interface with real-time status updates.
    • New functionality for managing wireless network configurations, including connection handling and configuration updates.
    • Added cookie management functionality for improved user settings persistence.
    • New configuration option for displaying favorites within the application.
  • Improvements

    • Enhanced network configuration with refined DNS, VLAN, and IPv6 setups.
    • Updated dashboard and UI elements—including a scroll-to-bottom button and external link prompts—for a more intuitive experience.
    • Modernized styling across themes for cleaner layouts and improved readability.
    • Improved handling of network interface configurations and commands for better reliability.
    • Streamlined visibility management for various network settings and improved user interactions.
    • Enhanced error handling and status reporting for network services.
    • Updated copyright notices across various components to reflect the new years (2025).

- Support Precision Time Protocol (PTP) - time accuracy in nanoseconds
- PTP requires a local time server which supports the PTP protocol
- Show only the applicable fields for the selected time sync value (animated)
- Add 'hints' to help the user in configuration
- Do not restart time service when only date/time format is updated

Note for Tom: to make PTP work the package "linuxptp-4.2-x86_64-1_BW.txz" needs to be installed with stock Unraid
- Add PTP support for bonded interfaces
- Add PTP support for bonded interfaces
- Disable PTP for bridge interfaces (unsupported)
- Give warning when no suitable interfaces are present for PTP
- Add Time State information
- Add time state info
- Better time state handling
- Add more info to NTP/PTP server message

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
emhttp/plugins/dynamix/DashStats.page (1)

1438-1474: Enhance security in VM clone functionality.

The VM clone function should include input validation and sanitization to prevent potential security issues.

function VMClone(uuid, name){
+  // Validate input parameters
+  if (!uuid || typeof uuid !== 'string' || !name || typeof name !== 'string') {
+    console.error('Invalid parameters for VM clone');
+    return;
+  }
+
+  // Sanitize input to prevent XSS
+  name = $('<div>').text(name).html();
+
  box = $("#iframe-popup");
  box.html($("#templateClone").html());
  box.find('#VMBeingCloned').html(name).change();
-  box.find('#target').val(name + "_clone");
+  box.find('#target').val(name + "_clone").attr('maxlength', 64);
  document.getElementById("Free").checked = true;
  document.getElementById("Overwrite").checked = true;
  
  // ... rest of the function
}
🧹 Nitpick comments (2)
emhttp/plugins/dynamix/DashStats.page (2)

672-674: Optimize tile visibility logic.

The current implementation checks each tile's visibility individually. Consider combining these checks for better performance.

-if (count['db_box1']>0) $('div#tile1').show(); else $('div#tile1').hide();
-if (count['db_box2']>0) $('div#tile2').show(); else $('div#tile2').hide();
-if (count['db_box3']>0) $('div#tile3').show(); else $('div#tile3').hide();
+// Use a single jQuery call for better performance
+$('div[id^="tile"]').each(function(index) {
+  $(this).toggle(count['db_box' + (index + 1)] > 0);
+});

904-914: Add validation and error handling to chart functions.

The addChartCpu function lacks input validation and error handling for edge cases.

function addChartCpu(load) {
+  // Validate input
+  if (typeof load !== 'number' || isNaN(load)) {
+    console.error('Invalid load value:', load);
+    return;
+  }
+
+  // Ensure load is within valid range
+  load = Math.max(0, Math.min(100, load));
+
  cputime++;
  var i = cpu.length - cpuline;
  if (i > 0) { // clear value outside graph
    i = i - 1;
    cpu[i].x = cputime - cpuline;
    cpu[i].y = 0;
  }
  cpu.push({x:cputime, y:load});
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 885a4cc and 5e8d17b.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix/DashStats.page (35 hunks)
🔇 Additional comments (2)
emhttp/plugins/dynamix/DashStats.page (2)

842-844: 🛠️ Refactor suggestion

Add error handling to cookie saving mechanism.

The current implementation doesn't handle potential failures in the AJAX request or validate the cookie data before saving.

function saveCookie() {
+  // Validate cookie data
+  if (!cookie || typeof cookie !== 'object') {
+    console.error('Invalid cookie data');
+    return;
+  }
+
+  // Save cookie with error handling
  $.post('/webGui/include/InitCharts.php',{
    cmd:'cookie',
    data:JSON.stringify(cookie)
+  }).fail(function(jqXHR, textStatus, errorThrown) {
+    console.error('Failed to save cookie:', textStatus, errorThrown);
  });
}

Likely invalid or redundant comment.


846-867: 🛠️ Refactor suggestion

Remove unused function.

The sanitizeMultiCookie function appears to be unused after the transition to the new cookie management system.

-function sanitizeMultiCookie(cookieName, delimiter, removeDuplicates=false) {
-  // Some browser states leave multi-value cookies with nulls, empties or duplicates.
-  // This function cleans up any such cookies so that they do not break functionality.
-  try {
-    var uncleanCookie = $.cookie(cookieName);
-    if (uncleanCookie) {
-      uncleanCookie = uncleanCookie.split(delimiter);
-      var cleanCookie = uncleanCookie.filter(n => n);
-      if (removeDuplicates) { cleanCookie = [...new Set(cleanCookie)]; }
-      if (JSON.stringify(uncleanCookie) !== JSON.stringify(cleanCookie)) {
-        $.cookie(cookieName,cleanCookie.join(delimiter),{expires:3650});
-        return true;
-      } else {
-        return false;
-      }
-    } else {
-      return false;
-    }
-  } catch (ex) {
-    return false;
-  }
-}

Likely invalid or redundant comment.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (7)
emhttp/plugins/dynamix/include/Wireless.php (7)

16-22: ⚠️ Potential issue

Enhance security and error handling for configuration files.

The configuration file paths and their handling need improvements:

  1. Move sensitive file paths to a secure configuration file
  2. Add proper error handling for file operations

Apply this diff to improve error handling:

-$var   = '/var/local/emhttp/var.ini';
-$cfg   = '/boot/config/wireless.cfg';
-$ssl   = '/etc/rc.d/rc.ssl.input';
-$tmp   = '/var/tmp/attr';
-$wifi  = is_readable($cfg) ? (array)parse_ini_file($cfg,true) : [];
-$attr  = is_readable($tmp) ? (array)parse_ini_file($tmp,true) : [];
-$md5   = md5(json_encode($attr),true);

+// Load configuration paths from a secure location
+$config = require_once '/etc/unraid/config.php';
+$var    = $config['var_path'];
+$cfg    = $config['wireless_config'];
+$ssl    = $config['ssl_input'];
+$tmp    = $config['temp_attr'];
+
+// Initialize with proper error handling
+$wifi = [];
+$attr = [];
+try {
+    if (is_readable($cfg)) {
+        $result = @parse_ini_file($cfg, true);
+        if ($result === false) {
+            error_log("Failed to parse wireless config file: $cfg");
+        } else {
+            $wifi = (array)$result;
+        }
+    }
+    if (is_readable($tmp)) {
+        $result = @parse_ini_file($tmp, true);
+        if ($result === false) {
+            error_log("Failed to parse wireless attributes file: $tmp");
+        } else {
+            $attr = (array)$result;
+        }
+    }
+    $md5 = md5(json_encode($attr),true);
+} catch (Exception $e) {
+    error_log("Error loading wireless configuration: " . $e->getMessage());
+}

36-52: ⚠️ Potential issue

Add input validation and error handling in scanWifi function.

The function needs proper validation of the port parameter and error handling for the shell command execution.

Apply this diff to improve the function:

 function scanWifi($port) {
+  if (!preg_match('/^[a-zA-Z0-9]+$/', $port)) {
+    error_log("Invalid wireless interface name: $port");
+    return [];
+  }
+  if (!file_exists("/sys/class/net/$port")) {
+    error_log("Wireless interface not found: $port");
+    return [];
+  }
   $wlan = [];
-  exec("iw ".escapeshellarg($port)." scan | grep -P '^BSS|signal:|SSID:|Authentication suites:'",$scan);
+  $result = 0;
+  exec("iw ".escapeshellarg($port)." scan | grep -P '^BSS|signal:|SSID:|Authentication suites:'",$scan, $result);
+  if ($result !== 0) {
+    error_log("Failed to scan wireless networks on interface: $port");
+    return [];
+  }

54-62: ⚠️ Potential issue

Add error handling in saveWifi function.

The function should handle potential file write failures.

Apply this diff to improve error handling:

 function saveWifi() {
   global $cfg, $wifi;
   $text = [];
   foreach ($wifi as $network => $block) {
     $text[] = "[$network]";
     foreach ($block as $key => $value) $text[] = "$key=\"$value\"";
   }
-  file_put_contents_atomic($cfg,implode("\n",$text)."\n");
+  try {
+    if (!file_put_contents_atomic($cfg,implode("\n",$text)."\n")) {
+      throw new Exception("Failed to write WiFi configuration");
+    }
+  } catch (Exception $e) {
+    error_log("Error saving WiFi configuration: " . $e->getMessage());
+    return false;
+  }
+  return true;
 }

122-124: ⚠️ Potential issue

Enhance security for password handling.

The password decryption lacks proper validation and error handling.

Apply this diff to improve security:

-  $user    = _var($wifi[$ssid],'USERNAME') && isset($cipher,$key,$iv) ? openssl_decrypt($wifi[$ssid]['USERNAME'],$cipher,$key,0,$iv) : _var($wifi[$ssid],'USERNAME');
-  $passwd  = _var($wifi[$ssid],'PASSWORD') && isset($cipher,$key,$iv) ? openssl_decrypt($wifi[$ssid]['PASSWORD'],$cipher,$key,0,$iv) : _var($wifi[$ssid],'PASSWORD');
+  $user = '';
+  $passwd = '';
+  if (_var($wifi[$ssid],'USERNAME') && isset($cipher, $key, $iv)) {
+    $decrypted = openssl_decrypt($wifi[$ssid]['USERNAME'],$cipher,$key,0,$iv);
+    if ($decrypted === false) {
+      error_log("Failed to decrypt username for network: $ssid");
+    } else {
+      $user = $decrypted;
+    }
+  }
+  if (_var($wifi[$ssid],'PASSWORD') && isset($cipher, $key, $iv)) {
+    $decrypted = openssl_decrypt($wifi[$ssid]['PASSWORD'],$cipher,$key,0,$iv);
+    if ($decrypted === false) {
+      error_log("Failed to decrypt password for network: $ssid");
+    } else {
+      $passwd = $decrypted;
+    }
+  }

151-159: ⚠️ Potential issue

Add CSRF token validation.

The form includes a CSRF token but there's no validation before processing the form data.

Add token validation before processing the form:

+  // Add at the beginning of the switch statement
+  if ($cmd !== 'list') {
+    $stored_token = parse_ini_file($var)['csrf_token'] ?? '';
+    if (!hash_equals($stored_token, $_POST['csrf_token'])) {
+      error_log("CSRF token validation failed");
+      http_response_code(403);
+      exit("Access denied");
+    }
+  }

185-187: ⚠️ Potential issue

Add input validation for IP addresses.

The form accepts IP addresses without validation.

Apply this diff to add validation:

-  echo "<tr class=\"static4 $hide2\"><td>"._('IPv4 address').":</td><td><input type=\"text\" name=\"IP4\" class=\"narrow\" maxlength=\"15\" autocomplete=\"off\" spellcheck=\"false\" value=\"$ip4\">/<select name=\"MASK4\" class=\"slim\">";
+  echo "<tr class=\"static4 $hide2\"><td>"._('IPv4 address').":</td><td><input type=\"text\" name=\"IP4\" class=\"narrow\" maxlength=\"15\" pattern=\"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$\" title=\""._('Enter a valid IPv4 address')."\" autocomplete=\"off\" spellcheck=\"false\" value=\"$ip4\">/<select name=\"MASK4\" class=\"slim\">";

211-215: ⚠️ Potential issue

Add validation before network removal.

The 'forget' command should validate the network existence and user permissions before removal.

Apply this diff to add validation:

 case 'forget':
   $ssid = rawurldecode($_POST['ssid']);
+  if (!isset($wifi[$ssid])) {
+    error_log("Attempt to remove non-existent network: $ssid");
+    break;
+  }
   if ($wifi[$ssid]['GROUP'] == 'active') exec("/etc/rc.d/rc.wireless stop &>/dev/null &");
   unset($wifi[$ssid]);
   saveWifi();
   break;
🧹 Nitpick comments (2)
etc/rc.d/rc.wireless (2)

28-29: Add validation for configuration values.

The script sources configuration values without validation. Consider adding checks for required configuration values to fail early if essential settings are missing.

 [[ -r $INI ]] && . $INI
+# Validate required configuration values
+[[ -z $PORT ]] && log "Warning: PORT not configured in $INI"
 PORT=${PORT:-wlan0}
+# Validate interface existence
+[[ ! -e $SYSTEM/$PORT ]] && log "Warning: Interface $PORT does not exist"

82-125: Refactor IP address assignment for better maintainability.

The ipaddr_up function is handling multiple concerns (DHCP, static IP, DNS) and is quite long. Consider splitting it into smaller, focused functions.

+# Handle DHCP configuration
+dhcp_up() {
+  local OPTIONS="-q -n -p -t 10"
+  [[ -n $HOSTNAME ]] && OPTIONS="$OPTIONS -h $HOSTNAME"
+  [[ $DNS == yes ]] && OPTIONS="$OPTIONS -C resolv.conf"
+  [[ $IP == ipv4 ]] && OPTIONS="$OPTIONS -4"
+  [[ $IP == ipv6 ]] && OPTIONS="$OPTIONS -6"
+  if carrier_up $PORT; then
+    log "interface $PORT is UP, polling up to 60 sec for DHCP $IP server"
+    if ! run timeout 60 dhcpcd -w $OPTIONS $PORT; then
+      log "can't obtain IP address, continue polling in background on interface $PORT"
+      run dhcpcd -b $OPTIONS $PORT
+    fi
+  else
+    log "interface $PORT is DOWN, polling DHCP $IP server in background"
+    run dhcpcd -b $OPTIONS $PORT
+  fi
+}
+
+# Handle static IP configuration
+static_up() {
+  if carrier_up $PORT; then STATE="UP"; else STATE="DOWN"; fi
+  log "interface $PORT is $STATE, setting static $IP address"
+  ipv6_addr $PORT 0 1
+  if [[ $IP == ipv4 ]]; then
+    [[ -n $IP4 && -n $MASK4 ]] && run ip -4 addr add $(unzero $IP4)/$MASK4 dev $PORT metric 3004
+    [[ -n $GATEWAY4 ]] && run ip -4 route add default via $GATEWAY4 dev $PORT metric 3004
+  fi
+  if [[ $IP == ipv6 ]]; then
+    [[ -n $IP6 && -n $MASK6 ]] && run ip -6 addr add $(unzero6 $IP6)/$MASK6 dev $PORT metric 3004
+    [[ -n $GATEWAY6 ]] && run ip -6 route add default via $GATEWAY6 dev $PORT metric 3004
+  fi
+}
+
 ipaddr_up(){
   if [[ $DHCP == yes ]]; then
-    # bring up interface using DHCP/SLAAC
-    ipv6_addr 1 1
-    OPTIONS="-q -n -p -t 10"
-    [[ -n $HOSTNAME ]] && OPTIONS="$OPTIONS -h $HOSTNAME"
-    [[ $DNS == yes ]] && OPTIONS="$OPTIONS -C resolv.conf"
-    [[ $IP == ipv4 ]] && OPTIONS="$OPTIONS -4"
-    [[ $IP == ipv6 ]] && OPTIONS="$OPTIONS -6"
-    if carrier_up $PORT; then
-      # interface is UP
-      log "interface $PORT is UP, polling up to 60 sec for DHCP $IP server"
-      if ! run timeout 60 dhcpcd -w $OPTIONS $PORT; then
-        log "can't obtain IP address, continue polling in background on interface $PORT"
-        run dhcpcd -b $OPTIONS $PORT
-      fi
-    else
-      # interface is DOWN
-      log "interface $PORT is DOWN, polling DHCP $IP server in background"
-      run dhcpcd -b $OPTIONS $PORT
-    fi
+    dhcp_up
   elif [[ $DHCP == no ]]; then
-    # bring up interface using static IP address
-    if carrier_up $PORT; then STATE="UP"; else STATE="DOWN"; fi
-    log "interface $PORT is $STATE, setting static $IP address"
-    ipv6_addr $PORT 0 1
-    if [[ $IP == ipv4 ]]; then
-      [[ -n $IP4 && -n $MASK4 ]] && run ip -4 addr add $(unzero $IP4)/$MASK4 dev $PORT metric 3004
-      [[ -n $GATEWAY4 ]] && run ip -4 route add default via $GATEWAY4 dev $PORT metric 3004
-    fi
-    if [[ $IP == ipv6 ]]; then
-      [[ -n $IP6 && -n $MASK6 ]] && run ip -6 addr add $(unzero6 $IP6)/$MASK6 dev $PORT metric 3004
-      [[ -n $GATEWAY6 ]] && run ip -6 route add default via $GATEWAY6 dev $PORT metric 3004
-    fi
+    static_up
   fi
   # ... rest of the function
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0cbf4d5 and bc3d1ea.

📒 Files selected for processing (3)
  • emhttp/plugins/dynamix/Wireless.page (1 hunks)
  • emhttp/plugins/dynamix/include/Wireless.php (1 hunks)
  • etc/rc.d/rc.wireless (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • emhttp/plugins/dynamix/Wireless.page
🔇 Additional comments (6)
etc/rc.d/rc.wireless (6)

31-42: Add input validation for IP address formatting functions.

The unzero and unzero6 functions lack input validation and may not handle edge cases properly.


44-57: Enhance security of SSL key generation.

The SSL key/iv generation uses hardcoded lengths and lacks error handling for dmidecode failures.


67-80: Add error handling for sysfs operations.

The IPv6 configuration functions don't verify if the sysfs writes succeed.


155-174: Enhance security of WPA configuration handling.

The wpa_configuration function writes sensitive data to the configuration file without proper file permissions management.


203-225: Add verification for network shutdown operations.

The wifi_stop function should verify the success of each operation.


227-269: Add error handling to the OpenSSL settings retrieval.

The function should handle cases where the SSL file is unreadable or decryption fails.

Comment thread emhttp/plugins/dynamix/DateTime.page
Comment thread emhttp/plugins/dynamix/include/InitCharts.php Outdated
Comment thread emhttp/plugins/dynamix/include/InitCharts.php

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
etc/rc.d/rc.wireless (3)

9-16: Consider using readonly for constant variables.

These variables appear to be constants and should be protected from modification.

-DAEMON="WiFi network"
-CALLER="wifi"
-INI="/var/local/emhttp/wireless.ini"
-CFG="/boot/config/wireless.cfg"
-SSLINPUT="/etc/rc.d/rc.ssl.input"
-STARTWIFI="/usr/local/emhttp/webGui/scripts/wireless"
-WPA="/etc/wpa_supplicant.conf"
+readonly DAEMON="WiFi network"
+readonly CALLER="wifi"
+readonly INI="/var/local/emhttp/wireless.ini"
+readonly CFG="/boot/config/wireless.cfg"
+readonly SSLINPUT="/etc/rc.d/rc.ssl.input"
+readonly STARTWIFI="/usr/local/emhttp/webGui/scripts/wireless"
+readonly WPA="/etc/wpa_supplicant.conf"

59-65: Add timeout configuration and improve error handling.

The carrier detection loop uses hardcoded values and could benefit from configurable timeouts.

+readonly CARRIER_TIMEOUT=${CARRIER_TIMEOUT:-10}
+readonly CARRIER_INTERVAL=${CARRIER_INTERVAL:-1}
 carrier_up(){
   local n
-  for n in {1..10}; do
+  for ((n=0; n<CARRIER_TIMEOUT; n++)); do
     [[ $(cat $SYSTEM/$1/carrier 2>/dev/null) == 1 ]] && return 0 || sleep 1
   done
+  log "Carrier detection timeout after ${CARRIER_TIMEOUT} seconds"
   return 1
 }

284-303: Add validation for command-line arguments.

The case statement should validate that exactly one argument is provided.

+if [[ $# -ne 1 ]]; then
+  echo "Error: Exactly one argument required"
+  echo "Usage: $BASENAME start|stop|join|restart|status"
+  exit 1
+fi
+
 case "$1" in
   'start')
     wifi_start
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6db4ef7 and 125092f.

📒 Files selected for processing (3)
  • emhttp/plugins/dynamix/DashStats.page (37 hunks)
  • etc/rc.d/rc.docker (8 hunks)
  • etc/rc.d/rc.wireless (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • emhttp/plugins/dynamix/DashStats.page
  • etc/rc.d/rc.docker
🔇 Additional comments (5)
etc/rc.d/rc.wireless (5)

31-35: Add input validation for IPv4 address format.

The unzero function lacks input validation which could lead to unexpected behavior with invalid IPv4 addresses.


67-74: Add error handling for sysfs operations.

The IPv6 configuration function doesn't verify if the sysfs writes succeed.


135-138: Fix IPv6 route flushing condition.

There appears to be a typo in the IPv6 route flushing condition.


152-171: Enhance security of WPA configuration handling.

The WPA configuration function writes sensitive data to the configuration file without proper file permissions.


234-235: Add error handling for failed decryption.

The password decryption doesn't validate success which could lead to connection attempts with invalid credentials.

Comment thread etc/rc.d/rc.wireless
Comment thread etc/rc.d/rc.wireless

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (3)
emhttp/plugins/dynamix.docker.manager/include/DockerClient.php (3)

339-340: Ensure $ct['Networks'] is not empty before using reset()

To prevent potential errors when accessing 'IPAddress', confirm that $ct['Networks'] is not empty before calling reset(). This ensures the code does not produce an error if no networks are defined for the container.

Apply this diff to add a check:

+if (!empty($ct['Networks'])) {
    $ip = reset($ct['Networks'])['IPAddress'];
+} else {
+   $ip = null;
+}

1011-1018: Ensure $driver[$netName] index exists before accessing

When accessing $driver[$netName], ensure the key exists to avoid undefined index notices. This can be achieved by adding an isset() check.

Apply this diff to add the check:

-if ($driver[$netName]=='ipvlan' || $driver[$netName]=='macvlan') {
+if (isset($driver[$netName]) && ($driver[$netName]=='ipvlan' || $driver[$netName]=='macvlan')) {

1090-1094: Avoid using extract() to prevent potential variable collisions

Using extract() can lead to unexpected variable overwriting and reduces code readability. It's safer to assign variables explicitly.

Apply this diff to assign variables explicitly:

-public static function ensureImageTag($image): string
-{
-    extract(static::parseImageTag($image));
-    return "$strRepo:$strTag";
+public static function ensureImageTag($image): string
+{
+    $parsed = static::parseImageTag($image);
+    $strRepo = $parsed['strRepo'];
+    $strTag = $parsed['strTag'];
+    return "$strRepo:$strTag";
 }
🧹 Nitpick comments (2)
emhttp/plugins/dynamix.docker.manager/include/DockerClient.php (2)

1009-1009: Consider avoiding reference assignment if not modifying the original

Assigning $ports by reference using & may not be necessary if you are not modifying $info['Config']['ExposedPorts']. Assigning by value improves code readability and reduces potential side effects.

Apply this diff to remove the reference:

-$ports = &$info['Config']['ExposedPorts'];
+$ports = $info['Config']['ExposedPorts'];

1173-1174: Consider using built-in PHP functions instead of exec

Using exec to execute shell commands can be avoided by leveraging PHP's built-in functions, improving portability and security.

Suggested alternative:

public static function cpus() {
    exec('cat /sys/devices/system/cpu/*/topology/thread_siblings_list|sort -nu', $cpus);
    return $cpus;
}

Can be refactored to:

public static function cpus() {
    $cpus = [];
    foreach (glob('/sys/devices/system/cpu/*/topology/thread_siblings_list') as $filename) {
        $content = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        $cpus = array_merge($cpus, $content);
    }
    $cpus = array_unique($cpus);
    sort($cpus, SORT_NUMERIC);
    return $cpus;
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc7703f and e162795.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.docker.manager/include/DockerClient.php (1 hunks)
🔇 Additional comments (5)
emhttp/plugins/dynamix.docker.manager/include/DockerClient.php (5)

39-43: Dynamic selection of network interface enhances robustness

The updated logic correctly selects the network interface by checking for 'br0', 'bond0', and defaults to 'eth0', improving adaptability to various network configurations. The fallback to 'wlan0' ensures wireless interfaces are considered when no wired interfaces are available.


682-684: Verify that adding 'Mode' to XPath query does not skip matching elements

By including [@Mode='$mode'] in the XPath query, the code may skip matching Config elements that lack the Mode attribute. Ensure this change does not prevent updating existing port configurations and that all relevant Config elements have the Mode attribute set appropriately.


990-992: Properly retrieve additional container labels

The added lines correctly extract net.unraid.docker.tailscale.hostname, net.unraid.docker.shell, and net.unraid.docker.managed labels from the container configuration, enhancing container metadata handling.


1096-1116: Efficient parsing of image tags implemented

The method parseImageTag correctly parses Docker image tags, handling cases like images starting with sha256 and images without a tag by defaulting to latest.


1120-1132: Robust image splitting logic implemented

The splitImage method effectively parses the image string using regular expressions and handles edge cases appropriately, returning null when unprocessable.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
emhttp/plugins/dynamix/include/Wrappers.php (1)

259-263: Consider using multiple connectivity check endpoints.

The check_network_connectivity function relies solely on Microsoft's NCSI endpoint. Consider adding fallback endpoints for more reliable connectivity checks.

 function check_network_connectivity(): bool {
-  $url = 'http://www.msftncsi.com/ncsi.txt';
-  $out = http_get_contents($url);
-  return ($out=="Microsoft NCSI");
+  $endpoints = [
+    ['url' => 'http://www.msftncsi.com/ncsi.txt', 'expected' => 'Microsoft NCSI'],
+    ['url' => 'http://www.apple.com/library/test/success.html', 'expected' => 'Success'],
+    ['url' => 'http://connectivity-check.ubuntu.com/', 'expected' => 'NetworkManager is online']
+  ];
+  
+  foreach ($endpoints as $endpoint) {
+    $out = http_get_contents($endpoint['url']);
+    if ($out === $endpoint['expected']) return true;
+  }
+  return false;
 }
🧹 Nitpick comments (2)
emhttp/plugins/dynamix/include/Wrappers.php (2)

52-58: Consider adding error handling for file operations.

The parse_plugin_cfg function performs file operations without explicit error handling. While it uses the null coalescing operator for the array merge, it might be good to add logging for file access failures.

 function parse_plugin_cfg($plugin, $sections=false, $scanner=INI_SCANNER_NORMAL) {
   global $docroot;
   $ram = "$docroot/plugins/$plugin/default.cfg";
   $rom = "/boot/config/plugins/$plugin/$plugin.cfg";
-  $cfg = file_exists($ram) ? my_parse_ini_file($ram, $sections, $scanner) : [];
-  return file_exists($rom) ? array_replace_recursive($cfg, my_parse_ini_file($rom, $sections, $scanner)) : $cfg;
+  try {
+    $cfg = file_exists($ram) ? my_parse_ini_file($ram, $sections, $scanner) : [];
+    return file_exists($rom) ? array_replace_recursive($cfg, my_parse_ini_file($rom, $sections, $scanner)) : $cfg;
+  } catch (Exception $e) {
+    my_logger("Error parsing plugin configuration for $plugin: " . $e->getMessage());
+    return [];
+  }
 }

175-191: Consider caching NVMe information for performance.

The get_nvme_info function makes multiple system calls which could be expensive if called frequently. Consider caching the results for a short period.

+private static $nvme_cache = [];
+private static $nvme_cache_ttl = 60; // 60 seconds
+
 function get_nvme_info($device, $info) {
+  $cache_key = "{$device}_{$info}";
+  if (isset(self::$nvme_cache[$cache_key]) && 
+      time() - self::$nvme_cache[$cache_key]['time'] < self::$nvme_cache_ttl) {
+    return self::$nvme_cache[$cache_key]['value'];
+  }
+
   switch ($info) {
   case 'temp':
     exec("nvme id-ctrl /dev/$device 2>/dev/null | grep -Pom2 '^[wc]ctemp +: \K\d+'",$temp);
-    return [$temp[0]-273, $temp[1]-273];
+    $result = [$temp[0]-273, $temp[1]-273];
+    break;
   // ... rest of the cases ...
   }
+  
+  self::$nvme_cache[$cache_key] = [
+    'value' => $result,
+    'time' => time()
+  ];
+  return $result;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e162795 and bc830d7.

📒 Files selected for processing (5)
  • emhttp/plugins/dynamix.docker.manager/include/DockerClient.php (7 hunks)
  • emhttp/plugins/dynamix.docker.manager/include/DockerContainers.php (8 hunks)
  • emhttp/plugins/dynamix.docker.manager/include/Helpers.php (2 hunks)
  • emhttp/plugins/dynamix/include/Wrappers.php (8 hunks)
  • sbin/create_network_ini (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • emhttp/plugins/dynamix.docker.manager/include/Helpers.php
🚧 Files skipped from review as they are similar to previous changes (2)
  • sbin/create_network_ini
  • emhttp/plugins/dynamix.docker.manager/include/DockerContainers.php
🔇 Additional comments (4)
emhttp/plugins/dynamix/include/Wrappers.php (2)

2-3: LGTM! Copyright years updated.

Copyright years have been consistently updated to 2025 for both Lime Technology and Bergware International.


265-269: LGTM! Well-structured network port check function.

The lan_port function efficiently checks both port existence and carrier state with proper error handling.

emhttp/plugins/dynamix.docker.manager/include/DockerClient.php (2)

2-3: LGTM! Copyright years updated.

The copyright years have been appropriately updated to reflect the current year.


1170-1183: LGTM! Well-structured network utility methods.

The new port() and host() methods provide a robust implementation for:

  • Prioritized network interface detection (br0 → bond0 → eth0 → wlan0)
  • Fallback to wlan0 when appropriate
  • Clean IP address retrieval using standard Linux tools

global $host;
$port = &$ct['Ports'][0];
$myIP = $myIP ?: $this->getTemplateValue($ct['Image'],'MyIP') ?: (_var($ct,'NetworkMode')=='host'||_var($port,'NAT') ? $host : (_var($port,'IP') ?: DockerUtil::myIP($ct['Name'])));
$myIP = $myIP ?: $this->getTemplateValue($ct['Image'],'MyIP') ?: (_var($ct,'NetworkMode')=='host'||_var($port,'NAT') ? DockerUtils::host() : (_var($port,'IP') ?: DockerUtil::myIP($ct['Name'])));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo in class name.

There's a typo in the class name: DockerUtils::host() should be DockerUtil::host() (without the 's').

Apply this diff to fix the typo:

-$myIP = $myIP ?: $this->getTemplateValue($ct['Image'],'MyIP') ?: (_var($ct,'NetworkMode')=='host'||_var($port,'NAT') ? DockerUtils::host() : (_var($port,'IP') ?: DockerUtil::myIP($ct['Name'])));
+$myIP = $myIP ?: $this->getTemplateValue($ct['Image'],'MyIP') ?: (_var($ct,'NetworkMode')=='host'||_var($port,'NAT') ? DockerUtil::host() : (_var($port,'IP') ?: DockerUtil::myIP($ct['Name'])));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$myIP = $myIP ?: $this->getTemplateValue($ct['Image'],'MyIP') ?: (_var($ct,'NetworkMode')=='host'||_var($port,'NAT') ? DockerUtils::host() : (_var($port,'IP') ?: DockerUtil::myIP($ct['Name'])));
$myIP = $myIP ?: $this->getTemplateValue($ct['Image'],'MyIP') ?: (_var($ct,'NetworkMode')=='host'||_var($port,'NAT') ? DockerUtil::host() : (_var($port,'IP') ?: DockerUtil::myIP($ct['Name'])));

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
emhttp/plugins/dynamix.docker.manager/include/DockerClient.php (2)

2-3: Update copyright years.

The copyright years have been updated to 2025, but line 4 still shows 2021 as the end year.

Apply this diff to maintain consistency in copyright years:

 /* Copyright 2005-2025, Lime Technology
  * Copyright 2012-2025, Bergware International.
- * Copyright 2014-2021, Guilherme Jardim, Eric Schultz, Jon Panozzo.
+ * Copyright 2014-2025, Guilherme Jardim, Eric Schultz, Jon Panozzo.

1171-1177: Add error handling and logging to port() method.

The port() method provides a clean interface selection mechanism but could benefit from error handling and logging.

Consider adding logging to track interface selection:

 public static function port() {
+    $logger = new Logger('docker');
     if (lan_port('br0')) {
+        $logger->info('Using br0 interface');
         return 'br0';
     }
     if (lan_port('bond0')) {
+        $logger->info('Using bond0 interface');
         return 'bond0';
     }
     if (lan_port('eth0')) {
+        $logger->info('Using eth0 interface');
         return 'eth0';
     }
     if (lan_port('wlan0')) {
+        $logger->info('Using wlan0 interface');
         return 'wlan0';
     }
+    $logger->warning('No valid network interface found');
     return '';
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe61abe and ee5409d.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.docker.manager/include/DockerClient.php (7 hunks)
🔇 Additional comments (2)
emhttp/plugins/dynamix.docker.manager/include/DockerClient.php (2)

17-17: LGTM! Proper dependency ordering.

Moving the Helpers.php require statement before class usage ensures proper initialization.


951-952: LGTM! Good encapsulation of network interface logic.

The use of DockerUtil::driver() and DockerUtil::host() centralizes network interface handling, improving maintainability.

Comment on lines +1179 to +1184
public static function host() {
$port = static::port();
if (!$port) return '';
$port = lan_port($port,true)==0 && lan_port('wlan0') ? 'wlan0' : $port;
return exec("ip -br -4 addr show $port scope global | sed -r 's/\/[0-9]+//g' | awk '{print $3;exit}'");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add IP address validation to host() method.

The host() method should validate the IP address format and handle command failures.

Apply this diff to add validation:

 public static function host() {
     $port = static::port();
     if (!$port) return '';
     $port = lan_port($port,true)==0 && lan_port('wlan0') ? 'wlan0' : $port;
-    return exec("ip -br -4 addr show $port scope global | sed -r 's/\/[0-9]+//g' | awk '{print $3;exit}'");
+    $ip = exec("ip -br -4 addr show $port scope global | sed -r 's/\/[0-9]+//g' | awk '{print $3;exit}'", $output, $return_var);
+    if ($return_var !== 0) {
+        error_log("Failed to get IP address for interface $port");
+        return '';
+    }
+    if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
+        error_log("Invalid IPv4 address format: $ip");
+        return '';
+    }
+    return $ip;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static function host() {
$port = static::port();
if (!$port) return '';
$port = lan_port($port,true)==0 && lan_port('wlan0') ? 'wlan0' : $port;
return exec("ip -br -4 addr show $port scope global | sed -r 's/\/[0-9]+//g' | awk '{print $3;exit}'");
}
public static function host() {
$port = static::port();
if (!$port) return '';
$port = lan_port($port, true) == 0 && lan_port('wlan0') ? 'wlan0' : $port;
$ip = exec("ip -br -4 addr show $port scope global | sed -r 's/\/[0-9]+//g' | awk '{print $3;exit}'", $output, $return_var);
if ($return_var !== 0) {
error_log("Failed to get IP address for interface $port");
return '';
}
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
error_log("Invalid IPv4 address format: $ip");
return '';
}
return $ip;
}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
emhttp/plugins/dynamix.docker.manager/include/DockerClient.php (1)

1179-1184: 🛠️ Refactor suggestion

Add error handling and validation to host() method.

The host() method should include error handling for command execution failures and IP address validation.

Apply this diff to improve error handling:

 public static function host() {
     $port = static::port();
     if (!$port) return '';
     $port = lan_port($port,true)==0 && lan_port('wlan0') ? 'wlan0' : $port;
-    return exec("ip -br -4 addr show $port scope global | sed -r 's/\/[0-9]+//g' | awk '{print $3;exit}'");
+    $ip = exec("ip -br -4 addr show $port scope global | sed -r 's/\/[0-9]+//g' | awk '{print $3;exit}'", $output, $return_var);
+    if ($return_var !== 0) {
+        error_log("Failed to get IP address for interface $port");
+        return '';
+    }
+    if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
+        error_log("Invalid IPv4 address format: $ip");
+        return '';
+    }
+    return $ip;
 }
🧹 Nitpick comments (2)
emhttp/plugins/dynamix.docker.manager/include/DockerClient.php (2)

1153-1153: Improve error handling in custom() method.

The custom() method should handle the case where the docker network ls command fails.

Apply this diff to add error handling:

-    return static::docker("network ls --filter driver='bridge' --filter driver='macvlan' --filter driver='ipvlan' --format='{{.Name}}' 2>/dev/null | grep -v '^bridge$'",true);
+    $result = static::docker("network ls --filter driver='bridge' --filter driver='macvlan' --filter driver='ipvlan' --format='{{.Name}}' 2>/dev/null | grep -v '^bridge$'",true);
+    if (!is_array($result)) {
+        error_log("Failed to get custom network list");
+        return [];
+    }
+    return $result;

1163-1164: Add error handling to cpus() method.

The cpus() method should handle the case where the command fails or the file is not accessible.

Apply this diff to add error handling:

-    exec('cat /sys/devices/system/cpu/*/topology/thread_siblings_list | sort -nu', $cpus);
-    return $cpus;
+    if (@exec('cat /sys/devices/system/cpu/*/topology/thread_siblings_list | sort -nu', $cpus, $return_var) === false || $return_var !== 0) {
+        error_log("Failed to get CPU topology information");
+        return [];
+    }
+    return $cpus;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee5409d and dcc95fe.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix.docker.manager/include/DockerClient.php (8 hunks)
🔇 Additional comments (4)
emhttp/plugins/dynamix.docker.manager/include/DockerClient.php (4)

2-3: LGTM: Copyright years updated.

The copyright years have been updated to include 2025.


1171-1177: LGTM: Network interface detection method.

The port() method provides a clear priority order for network interfaces (br0 > bond0 > eth0 > wlan0).


17-17: Verify the dependency order change.

The Helpers.php inclusion has been moved before other includes. Ensure this doesn't create any dependency issues with functions or classes defined in other included files.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Check for any dependencies in Helpers.php that might require other includes
rg -A 5 "require|include" "plugins/dynamix.docker.manager/include/Helpers.php"

Length of output: 169


Let's gather more information about the location of Helpers.php to ensure we're inspecting the correct file. It appears that the previous command couldn’t find the file at
plugins/dynamix.docker.manager/include/Helpers.php. We should verify whether the file exists elsewhere in the repository.

To do that, please run the following shell script:


🏁 Script executed:

#!/bin/bash
# Search for any file named Helpers.php in the repository
find . -type f -name "Helpers.php"

Length of output: 139


Dependency order verified as safe.

After verifying the location and contents of the Helpers.php file (located at emhttp/plugins/dynamix.docker.manager/include/Helpers.php), the file shows no additional require or include statements that would create dependency issues when loaded before other includes. The absence of internal dependencies in Helpers.php indicates that moving its inclusion earlier does not adversely affect the later code dependencies.


271-271: Verify the IP address determination logic.

The IP address determination logic has been updated to use DockerUtil::host(). Ensure this change maintains backward compatibility with existing container configurations.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Check for any existing containers that might be affected by the IP address change
ast-grep --pattern $'$myIP = $_ ?: $this->getTemplateValue($ct[\'Image\'],\'MyIP\') ?: (_var($ct,\'NetworkMode\')==\'host\'||_var($port,\'NAT\') ? DockerUtil::host() : (_var($port,\'IP\') ?: DockerUtil::myIP($ct[\'Name\'])))'

Length of output: 254


I'll gather additional context by inspecting a few lines around the IP determination logic in the file so we can assess the complete context and verify backward compatibility.


🏁 Script executed:

#!/bin/bash
# Print lines 265-275 from the file to inspect the surrounding context of the IP logic.
sed -n '265,275p' emhttp/plugins/dynamix.docker.manager/include/DockerClient.php

Length of output: 639


IP Address Determination Logic Verified

The examined assignment preserves backward compatibility:

  • The logic first uses any preexisting IP in $myIP or the template value for 'MyIP'.
  • When neither is defined, it correctly falls back to using DockerUtil::host() if either the container’s network mode is 'host' or the NAT flag is set.
  • If those conditions aren’t met, it then checks for an IP in $port or, as a last resort, calls DockerUtil::myIP().

All intended conditions are represented, and existing container configurations should remain unaffected.

@limetech limetech merged commit 22f7acb into unraid:master Feb 6, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Feb 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants