From e6dd72c1147174c562cceec1f91311283cd86ed8 Mon Sep 17 00:00:00 2001 From: mgutt <10757176+mgutt@users.noreply.github.com> Date: Mon, 12 Jan 2026 00:19:26 +0100 Subject: [PATCH 1/3] Fix issue #2515: Clean up temporary terminal files - Use fixed filename 'file.manager.terminal.sh' instead of timestamp-based names - Remove temporary script after terminal is opened (not dependent on script success) - Use user's shell from posix_getpwuid() instead of hardcoded bash - Add sed_escape() function similar to preg_quote() with optional delimiter - Prevents accumulation of /var/tmp/File_Manager_*.run.sh files --- emhttp/plugins/dynamix/Browse.page | 3 +- .../plugins/dynamix/include/OpenTerminal.php | 32 +++++++++++++------ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/emhttp/plugins/dynamix/Browse.page b/emhttp/plugins/dynamix/Browse.page index 774dbc49c..06bd0bf89 100644 --- a/emhttp/plugins/dynamix/Browse.page +++ b/emhttp/plugins/dynamix/Browse.page @@ -1390,8 +1390,7 @@ function xlink(link) { }, function(isConfirm) { if (isConfirm === false) { // Terminal button was clicked (cancel button) - var d = new Date(); - openTerminal('ttyd', 'File_Manager_' + d.getTime(), path); + openTerminal('ttyd', 'file.manager.terminal', path); } // Ok button (isConfirm === true) or dialog dismissed - just close }); diff --git a/emhttp/plugins/dynamix/include/OpenTerminal.php b/emhttp/plugins/dynamix/include/OpenTerminal.php index a24115106..e22c02bd2 100644 --- a/emhttp/plugins/dynamix/include/OpenTerminal.php +++ b/emhttp/plugins/dynamix/include/OpenTerminal.php @@ -40,6 +40,15 @@ function command($path,$file) { global $run,$wait,$rows; return (file_exists($file) && substr($file,0,strlen($path))==$path) ? "$run tail -f -n $rows '$file'" : $wait; } +function sed_escape($s, $delimiter = null) { + // escape sed BRE meta characters: . * [ ] ^ $ \ + $escaped = preg_replace('/([.*\[\]^$\\\\])/', '\\\\$1', $s); + // additionally escape delimiter if provided + if ($delimiter !== null) { + $escaped = str_replace($delimiter, '\\' . $delimiter, $escaped); + } + return $escaped; +} switch ($_GET['tag']) { case 'ttyd': // check if ttyd already running @@ -65,29 +74,34 @@ function command($path,$file) { $real_path = '/root'; } - $name = unbundle($_GET['name']); - $exec = "/var/tmp/$name.run.sh"; + // Set script variables + $exec = "/var/tmp/file.manager.terminal.sh"; $escaped_path = str_replace("'", "'\\''", $real_path); - // Escape sed metacharacters: & (matched string), \\ (escape char), / (delimiter) - $sed_escaped = str_replace(['\\', '&', '/'], ['\\\\', '\\&', '\\/'], $escaped_path); + $sed_escaped = sed_escape($escaped_path, '#'); + + // Get user's shell (same as standard terminal) + $user_shell = posix_getpwuid(0)['shell']; // Create startup script similar to ~/.bashrc // Note: We can not use ~/.bashrc as it loads /etc/profile which does 'cd $HOME' + // Note: Script deletes itself before exec (bash has already loaded the script into memory) $script_content = << /tmp/$name.profile -source /tmp/$name.profile +sed 's#^cd \$HOME#cd '\''$sed_escaped'\''#' /etc/profile > /tmp/file.manager.terminal.profile +source /tmp/file.manager.terminal.profile source /root/.bash_profile 2>/dev/null -rm /tmp/$name.profile -exec bash --norc -i +rm /tmp/file.manager.terminal.profile +# Delete this script and exec shell (bash has already loaded this into memory) +{ rm -f '$exec'; exec $user_shell --norc -i; } BASH; file_put_contents($exec, $script_content); chmod($exec, 0755); exec("ttyd-exec -i '$sock' $exec"); + + // Standard login shell } else { - // Standard login shell if ($retval != 0) exec("ttyd-exec -i '$sock' '" . posix_getpwuid(0)['shell'] . "' --login"); } break; From 6e14b54348100c8460c58493bb77995914a23e5c Mon Sep 17 00:00:00 2001 From: mgutt <10757176+mgutt@users.noreply.github.com> Date: Mon, 12 Jan 2026 00:59:11 +0100 Subject: [PATCH 2/3] Fix sed replacement escaping for paths with ampersands --- emhttp/plugins/dynamix/include/OpenTerminal.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/emhttp/plugins/dynamix/include/OpenTerminal.php b/emhttp/plugins/dynamix/include/OpenTerminal.php index e22c02bd2..13fe4a7b4 100644 --- a/emhttp/plugins/dynamix/include/OpenTerminal.php +++ b/emhttp/plugins/dynamix/include/OpenTerminal.php @@ -40,8 +40,8 @@ function command($path,$file) { global $run,$wait,$rows; return (file_exists($file) && substr($file,0,strlen($path))==$path) ? "$run tail -f -n $rows '$file'" : $wait; } -function sed_escape($s, $delimiter = null) { - // escape sed BRE meta characters: . * [ ] ^ $ \ +function bre_escape($s, $delimiter = null) { + // escape BRE meta characters: . * [ ] ^ $ \ $escaped = preg_replace('/([.*\[\]^$\\\\])/', '\\\\$1', $s); // additionally escape delimiter if provided if ($delimiter !== null) { @@ -49,6 +49,10 @@ function sed_escape($s, $delimiter = null) { } return $escaped; } +function sed_escape($s) { + // escape sed replacement meta characters: & and \ + return str_replace(['\\', '&'], ['\\\\', '\\&'], $s); +} switch ($_GET['tag']) { case 'ttyd': // check if ttyd already running @@ -77,7 +81,7 @@ function sed_escape($s, $delimiter = null) { // Set script variables $exec = "/var/tmp/file.manager.terminal.sh"; $escaped_path = str_replace("'", "'\\''", $real_path); - $sed_escaped = sed_escape($escaped_path, '#'); + $sed_escaped = sed_escape($escaped_path); // Get user's shell (same as standard terminal) $user_shell = posix_getpwuid(0)['shell']; From 484f0914fcffd37d086eda75b2122d0e6c6e5a27 Mon Sep 17 00:00:00 2001 From: mgutt <10757176+mgutt@users.noreply.github.com> Date: Mon, 12 Jan 2026 01:03:35 +0100 Subject: [PATCH 3/3] Prevent race condition with unique file identifiers --- .../plugins/dynamix/include/OpenTerminal.php | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/emhttp/plugins/dynamix/include/OpenTerminal.php b/emhttp/plugins/dynamix/include/OpenTerminal.php index 13fe4a7b4..032165520 100644 --- a/emhttp/plugins/dynamix/include/OpenTerminal.php +++ b/emhttp/plugins/dynamix/include/OpenTerminal.php @@ -40,15 +40,6 @@ function command($path,$file) { global $run,$wait,$rows; return (file_exists($file) && substr($file,0,strlen($path))==$path) ? "$run tail -f -n $rows '$file'" : $wait; } -function bre_escape($s, $delimiter = null) { - // escape BRE meta characters: . * [ ] ^ $ \ - $escaped = preg_replace('/([.*\[\]^$\\\\])/', '\\\\$1', $s); - // additionally escape delimiter if provided - if ($delimiter !== null) { - $escaped = str_replace($delimiter, '\\' . $delimiter, $escaped); - } - return $escaped; -} function sed_escape($s) { // escape sed replacement meta characters: & and \ return str_replace(['\\', '&'], ['\\\\', '\\&'], $s); @@ -79,7 +70,9 @@ function sed_escape($s) { } // Set script variables - $exec = "/var/tmp/file.manager.terminal.sh"; + $unique_id = getmypid() . '_' . uniqid(); // prevent race condition with multiple terminals + $exec = "/var/tmp/file.manager.terminal.$unique_id.sh"; + $profile = "/tmp/file.manager.terminal.$unique_id.profile"; $escaped_path = str_replace("'", "'\\''", $real_path); $sed_escaped = sed_escape($escaped_path); @@ -92,10 +85,10 @@ function sed_escape($s) { $script_content = << /tmp/file.manager.terminal.profile -source /tmp/file.manager.terminal.profile +sed 's#^cd \$HOME#cd '\''$sed_escaped'\''#' /etc/profile > '$profile' +source '$profile' source /root/.bash_profile 2>/dev/null -rm /tmp/file.manager.terminal.profile +rm '$profile' # Delete this script and exec shell (bash has already loaded this into memory) { rm -f '$exec'; exec $user_shell --norc -i; } BASH;