From cc5f7bb90c1a7c91a04dd069e0f95db94f7503fc Mon Sep 17 00:00:00 2001
From: Frank Harris
Date: Sat, 6 Jun 2026 11:41:17 -0500
Subject: [PATCH] Agent update
---
Agent-Windows/Install/agent_start.bat | 106 +++++++++++-----
Agent-Windows/Install/agent_start_cygwin.sh | 116 ++++++++++++++++++
Agent-Windows/ogp_agent.pl | 6 +-
Agent_Linux/ogp_agent.pl | 24 +++-
Panel/modules/administration/panel_update.php | 36 ++----
.../gamemanager/home_handling_functions.php | 85 ++++++++++++-
Panel/modules/gamemanager/log.php | 19 ++-
Panel/modules/gamemanager/server_monitor.php | 4 +-
Panel/modules/gamemanager/view_server_log.php | 36 +++++-
docs/agents/LINUX_AGENT.md | 7 +-
docs/agents/WINDOWS_AGENT.md | 39 +++++-
docs/architecture/API_REFERENCE.md | 23 +++-
docs/features/LOGGING_SYSTEM.md | 10 +-
docs/features/STATUS_SYSTEM.md | 19 +++
docs/modules/GAMEMANAGER.md | 21 +++-
docs/modules/UPDATE.md | 8 +-
16 files changed, 474 insertions(+), 85 deletions(-)
create mode 100644 Agent-Windows/Install/agent_start_cygwin.sh
diff --git a/Agent-Windows/Install/agent_start.bat b/Agent-Windows/Install/agent_start.bat
index d04a11d1..7c2031a9 100644
--- a/Agent-Windows/Install/agent_start.bat
+++ b/Agent-Windows/Install/agent_start.bat
@@ -1,41 +1,79 @@
@echo off
-@title OGP Agent
-FOR /f "tokens=2,3,4 delims=[.]" %%a IN ('ver') DO SET WVer=%%a
-FOR /f "tokens=2,3 delims= " %%a IN ('echo %WVer%') DO SET Ver=%%a
+setlocal EnableExtensions
+title GSP Windows Agent
+
whoami /groups | find "S-1-16-12288" >nul 2>&1
-if NOT %errorLevel% == 0 if %VER% GEQ 6 (
- echo Failure: Current permissions inadequate.
- echo[
- echo Run this script by using "Run as administrator" in the context menu.
- pause >nul
- exit
+if not "%errorLevel%" == "0" (
+ echo Failure: current permissions are inadequate.
+ echo.
+ echo Run this script with "Run as administrator".
+ pause
+ exit /b 1
)
-set WD=%~dp0
-pushd %WD%
-set path=%WD%bin;%WD%usr\sbin;%path%
-set CYGWIN=server ntsec
-set SHELL=/bin/bash
-set runAgentNormally=no
-REM Stop any running agent
-if exist %WD%var\run\pure-ftpd.pid set /p PID1=<%WD%var\run\pure-ftpd.pid
-if exist %WD%OGP\ogp_agent.pid set /p PID2=<%WD%OGP\ogp_agent.pid
-if exist %WD%OGP\ogp_agent_run.pid set /p PID3=<%WD%OGP\ogp_agent_run.pid
-IF NOT [%PID1%] == [] kill -15 %PID1%
-IF NOT [%PID2%] == [] kill -15 %PID2%
-IF NOT [%PID3%] == [] kill -15 %PID3%
+set "WD=%~dp0"
+pushd "%WD%" >nul 2>&1
-REM Check for gameserver user and if it exists and the user running this script matches, run it the normal way, else prompt for elevation
-if "%username%" == "" set runAgentNormally=yes
-if "%username%" == "gameserver" set runAgentNormally=yes
-
-net user gameserver
-if %ERRORLEVEL% EQU 0 (
- if %runAgentNormally% == yes (
- bash ogp_agent -pidfile /OGP/ogp_agent_run.pid
- ) else (
- cygstart mintty /c "runas /profile /user:gameserver \"%WD%\bin\bash.exe %WD%\bin\ogp_agent -pidfile /OGP/ogp_agent_run.pid\""
+set "CYGWIN_ROOT=%WD%"
+if not exist "%CYGWIN_ROOT%OGP\ogp_agent.pl" (
+ if exist "%WD%..\OGP\ogp_agent.pl" (
+ for %%I in ("%WD%..") do set "CYGWIN_ROOT=%%~fI\"
)
-) else (
- bash ogp_agent -pidfile /OGP/ogp_agent_run.pid
)
+
+set "BASH_EXE="
+if exist "%CYGWIN_ROOT%bin\bash.exe" set "BASH_EXE=%CYGWIN_ROOT%bin\bash.exe"
+if not defined BASH_EXE if exist "C:\cygwin64\bin\bash.exe" set "BASH_EXE=C:\cygwin64\bin\bash.exe"
+if not defined BASH_EXE if exist "C:\cygwin\bin\bash.exe" set "BASH_EXE=C:\cygwin\bin\bash.exe"
+
+if not defined BASH_EXE (
+ echo Failure: Cygwin bash.exe was not found.
+ echo.
+ echo Checked:
+ echo %CYGWIN_ROOT%bin\bash.exe
+ echo C:\cygwin64\bin\bash.exe
+ echo C:\cygwin\bin\bash.exe
+ pause
+ exit /b 1
+)
+
+set "PATH=%CYGWIN_ROOT%bin;%CYGWIN_ROOT%usr\sbin;%PATH%"
+set "CYGWIN=server ntsec"
+set "SHELL=/bin/bash"
+
+set "HELPER=/Install/agent_start_cygwin.sh"
+if exist "%CYGWIN_ROOT%OGP\Install\agent_start_cygwin.sh" set "HELPER=/OGP/Install/agent_start_cygwin.sh"
+if not exist "%CYGWIN_ROOT%Install\agent_start_cygwin.sh" if not exist "%CYGWIN_ROOT%OGP\Install\agent_start_cygwin.sh" (
+ echo Failure: agent_start_cygwin.sh was not found.
+ echo.
+ echo Expected one of:
+ echo %CYGWIN_ROOT%Install\agent_start_cygwin.sh
+ echo %CYGWIN_ROOT%OGP\Install\agent_start_cygwin.sh
+ pause
+ exit /b 1
+)
+
+rem Stop any existing agent processes whose PID files still exist.
+if exist "%CYGWIN_ROOT%var\run\pure-ftpd.pid" set /p PID1=<"%CYGWIN_ROOT%var\run\pure-ftpd.pid"
+if exist "%CYGWIN_ROOT%OGP\ogp_agent.pid" set /p PID2=<"%CYGWIN_ROOT%OGP\ogp_agent.pid"
+if exist "%CYGWIN_ROOT%OGP\ogp_agent_run.pid" set /p PID3=<"%CYGWIN_ROOT%OGP\ogp_agent_run.pid"
+if defined PID1 kill -15 %PID1% >nul 2>&1
+if defined PID2 kill -15 %PID2% >nul 2>&1
+if defined PID3 kill -15 %PID3% >nul 2>&1
+
+echo Starting GSP Windows Agent with:
+echo %BASH_EXE%
+echo.
+"%BASH_EXE%" --login "%HELPER%" /OGP/ogp_agent_run.pid
+set "AGENT_EXIT=%ERRORLEVEL%"
+
+if not "%AGENT_EXIT%" == "0" (
+ echo.
+ echo GSP Windows Agent exited with error code %AGENT_EXIT%.
+ echo Review the messages above. The window is staying open so the failure is visible.
+ pause
+ exit /b %AGENT_EXIT%
+)
+
+popd >nul 2>&1
+exit /b 0
diff --git a/Agent-Windows/Install/agent_start_cygwin.sh b/Agent-Windows/Install/agent_start_cygwin.sh
new file mode 100644
index 00000000..2fc2060e
--- /dev/null
+++ b/Agent-Windows/Install/agent_start_cygwin.sh
@@ -0,0 +1,116 @@
+#!/usr/bin/env bash
+
+set -u
+
+AGENT_DIR="/OGP"
+PIDFILE="${1:-/OGP/ogp_agent_run.pid}"
+PREFS_FILE="$AGENT_DIR/Cfg/bash_prefs.cfg"
+REPO_URL_DEFAULT="http://forge.runlevelsystems.com/dev/GSP.git"
+REPO_BRANCH_DEFAULT="Panel-unstable"
+
+warn() {
+ printf 'WARNING: %s\n' "$*" >&2
+}
+
+fail() {
+ printf 'ERROR: %s\n' "$*" >&2
+ exit 1
+}
+
+normalize_text_files() {
+ local root="$1"
+ [ -d "$root" ] || return 0
+ find "$root" -type f \( -name '*.pl' -o -name '*.pm' -o -name '*.sh' -o -name '*.cfg' \) -print0 2>/dev/null |
+ while IFS= read -r -d '' file; do
+ sed -i 's/\r$//' "$file" 2>/dev/null || warn "Could not normalize line endings for $file"
+ done
+}
+
+load_agent_preferences() {
+ agent_auto_update=0
+ agent_update_repo_url="$REPO_URL_DEFAULT"
+ agent_update_branch="$REPO_BRANCH_DEFAULT"
+
+ if [ -f "$PREFS_FILE" ]; then
+ # shellcheck disable=SC1090
+ . "$PREFS_FILE"
+ fi
+
+ agent_auto_update="${agent_auto_update:-0}"
+ agent_update_repo_url="${agent_update_repo_url:-$REPO_URL_DEFAULT}"
+ agent_update_branch="${agent_update_branch:-$REPO_BRANCH_DEFAULT}"
+}
+
+auto_update_windows_agent() {
+ [ "$agent_auto_update" = "1" ] || {
+ echo "Agent auto-update is disabled."
+ return 0
+ }
+
+ if ! command -v git >/dev/null 2>&1; then
+ warn "git is not installed; skipping agent auto-update and using the current agent."
+ return 0
+ fi
+
+ local tmp_dir repo_dir source_file target_file backup_file
+ tmp_dir="$(mktemp -d /tmp/gsp-agent-update.XXXXXX 2>/dev/null)" || {
+ warn "Could not create temporary update directory; skipping auto-update."
+ return 0
+ }
+ repo_dir="$tmp_dir/repo"
+ target_file="$AGENT_DIR/ogp_agent.pl"
+ backup_file="$AGENT_DIR/ogp_agent.pl.bak.$(date +%Y%m%d%H%M%S)"
+
+ echo "Checking for Windows agent update from $agent_update_repo_url ($agent_update_branch)..."
+ if ! git clone --depth 1 --branch "$agent_update_branch" "$agent_update_repo_url" "$repo_dir"; then
+ warn "Agent auto-update clone failed; using the current agent."
+ rm -rf "$tmp_dir"
+ return 0
+ fi
+
+ source_file="$repo_dir/Agent-Windows/ogp_agent.pl"
+ if [ ! -f "$source_file" ]; then
+ warn "Updated Windows agent source was not found at Agent-Windows/ogp_agent.pl; using the current agent."
+ rm -rf "$tmp_dir"
+ return 0
+ fi
+
+ cp "$target_file" "$backup_file" 2>/dev/null || {
+ warn "Could not backup $target_file; skipping auto-update."
+ rm -rf "$tmp_dir"
+ return 0
+ }
+
+ if ! cp "$source_file" "$target_file"; then
+ warn "Could not copy updated Windows agent; restoring backup."
+ cp "$backup_file" "$target_file" 2>/dev/null
+ rm -rf "$tmp_dir"
+ return 0
+ fi
+
+ sed -i 's/\r$//' "$target_file" 2>/dev/null || true
+ if ! perl -c "$target_file"; then
+ warn "Updated Windows agent failed perl syntax validation; restoring backup."
+ cp "$backup_file" "$target_file" 2>/dev/null
+ perl -c "$target_file" || true
+ rm -rf "$tmp_dir"
+ return 0
+ fi
+
+ echo "Windows agent auto-update completed."
+ rm -rf "$tmp_dir"
+ return 0
+}
+
+cd "$AGENT_DIR" || fail "Could not enter $AGENT_DIR. Is the Windows agent installed under Cygwin /OGP?"
+
+normalize_text_files "$AGENT_DIR"
+normalize_text_files "/Install"
+load_agent_preferences
+auto_update_windows_agent
+
+echo "Validating $AGENT_DIR/ogp_agent.pl..."
+perl -c "$AGENT_DIR/ogp_agent.pl" || fail "Perl syntax/dependency validation failed. Install missing Cygwin Perl packages or restore a valid Windows agent file."
+
+echo "Launching GSP Windows Agent..."
+exec perl "$AGENT_DIR/ogp_agent.pl" -pidfile "$PIDFILE"
diff --git a/Agent-Windows/ogp_agent.pl b/Agent-Windows/ogp_agent.pl
index 72f2ac7f..c2ea2f00 100644
--- a/Agent-Windows/ogp_agent.pl
+++ b/Agent-Windows/ogp_agent.pl
@@ -749,7 +749,8 @@ sub is_port_listening_without_decrypt
my ($server_ip, $port) = @_;
return 0 unless(defined($port) && $port =~ /^[0-9]+$/ && $port > 0 && $port <= 65535);
my $out = `netstat -an 2>/dev/null`;
- return 1 if(defined($out) && $out =~ /[:.]$port\s+.*(LISTENING|UDP)/i);
+ return 1 if(defined($out) && $out =~ /^\s*TCP\s+\S+[:.]$port\s+\S+\s+LISTENING/im);
+ return 1 if(defined($out) && $out =~ /^\s*UDP\s+\S+[:.]$port\s+/im);
return 0;
}
@@ -797,7 +798,8 @@ sub server_status_without_decrypt
}
elsif($game_port_listening)
{
- $status = "UNRESPONSIVE";
+ $status = "ONLINE";
+ $ready = 1;
$last_error = "Game port is listening but the managed screen session is not running.";
}
else
diff --git a/Agent_Linux/ogp_agent.pl b/Agent_Linux/ogp_agent.pl
index e26a9011..1a90a487 100644
--- a/Agent_Linux/ogp_agent.pl
+++ b/Agent_Linux/ogp_agent.pl
@@ -44,9 +44,6 @@ use Archive::Extract; # Used to handle archived files.
use File::Find;
use Schedule::Cron; # Used for scheduling tasks
-# Database connectivity for resource stats
-use DBI; # Database interface for MySQL connection
-
# Compression tools
use IO::Compress::Bzip2 qw(bzip2 $Bzip2Error); # Used to compress files to bz2.
use Compress::Zlib; # Used to compress file download buffers to zlib.
@@ -957,7 +954,8 @@ sub server_status_without_decrypt
}
elsif($game_port_listening)
{
- $status = "UNRESPONSIVE";
+ $status = "ONLINE";
+ $ready = 1;
$last_error = "Game port is listening but the managed screen session is not running.";
}
else
@@ -3188,6 +3186,20 @@ sub mon_stats
return "1;$encoded_content";
}
+sub ensure_dbi_available
+{
+ eval {
+ require DBI;
+ DBI->import();
+ 1;
+ } and return 1;
+
+ my $error = $@ || "unknown error";
+ logger "DBI Perl module unavailable; resource stats database submission is disabled: $error";
+ scheduler_log_events("DBI Perl module unavailable; resource stats database submission is disabled");
+ return 0;
+}
+
sub submit_resource_stats_to_db
{
my ($cpu_usage, $mem_used, $mem_total, $mem_percent, $disk_used, $disk_total, $disk_free, $disk_percent, $uptime, $load_1min, $load_5min, $load_15min) = @_;
@@ -3202,6 +3214,10 @@ sub submit_resource_stats_to_db
return -1;
}
+ if (!ensure_dbi_available()) {
+ return -1;
+ }
+
my $dbh;
eval {
# Connect to MySQL database
diff --git a/Panel/modules/administration/panel_update.php b/Panel/modules/administration/panel_update.php
index 75dcd301..b3bae28e 100644
--- a/Panel/modules/administration/panel_update.php
+++ b/Panel/modules/administration/panel_update.php
@@ -134,6 +134,17 @@ $repo_root = gsp_detect_repo_root();
if (!$repo_root || !function_exists('exec')) {
return null;
}
+$out = [];
+$ret = 0;
+exec('git -C ' . escapeshellarg($repo_root) . ' rev-parse HEAD 2>/dev/null', $out, $ret);
+if ($ret === 0 && !empty($out[0])) {
+$sha = trim($out[0]);
+if (preg_match('/^[0-9a-f]{40,64}$/i', $sha)) {
+return $sha;
+}
+}
+return null;
+}
function gsp_update_settings()
{
@@ -169,17 +180,6 @@ $errors[] = 'Panel Path must point to the Panel folder inside Repository Root.';
}
return $errors;
}
-$out = [];
-$ret = 0;
-exec('git -C ' . escapeshellarg($repo_root) . ' rev-parse HEAD 2>/dev/null', $out, $ret);
-if ($ret === 0 && !empty($out[0])) {
-$sha = trim($out[0]);
-if (preg_match('/^[0-9a-f]{40,64}$/i', $sha)) {
-return $sha;
-}
-}
-return null;
-}
function gsp_read_version_json()
{
@@ -2254,19 +2254,7 @@ foreach ((array)$apache_scan_result['ssl_issues'] as $ssl_issue) {
echo htmlspecialchars($ssl_issue['vhost'] . ' ' . $ssl_issue['directive'] . ': ' . $ssl_issue['path'] . ' (' . $ssl_issue['reason'] . ')') . "
";
}
echo "
\n";
-echo "Renew certificate command: certbot --apache -d gameservers.world -d www.gameservers.world
";
-foreach ((array)$apache_scan_result['ssl_issues'] as $ssl_issue) {
-$vhost = basename((string)$ssl_issue['vhost']);
-if (strpos($vhost, '-ssl.conf') === false) {
-continue;
-}
-echo "";
-}
+echo "SSL certificate issues are diagnostic only and do not block Panel updates.
";
}
echo "