Agent update
This commit is contained in:
parent
c195c0930b
commit
cc5f7bb90c
16 changed files with 474 additions and 85 deletions
|
|
@ -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
|
||||
|
|
|
|||
116
Agent-Windows/Install/agent_start_cygwin.sh
Normal file
116
Agent-Windows/Install/agent_start_cygwin.sh
Normal file
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'] . ')') . "<br>";
|
||||
}
|
||||
echo "</p>\n";
|
||||
echo "<p style='color:#8a6d3b;'><strong>Renew certificate command:</strong> <code>certbot --apache -d gameservers.world -d www.gameservers.world</code></p>";
|
||||
foreach ((array)$apache_scan_result['ssl_issues'] as $ssl_issue) {
|
||||
$vhost = basename((string)$ssl_issue['vhost']);
|
||||
if (strpos($vhost, '-ssl.conf') === false) {
|
||||
continue;
|
||||
}
|
||||
echo "<form method='POST' style='display:inline-block;margin-right:8px;'>";
|
||||
echo "<input type='hidden' name='gsp_update_action' value='disable_ssl_vhost'>";
|
||||
echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($csrf_token) . "'>";
|
||||
echo "<input type='hidden' name='gsp_ssl_vhost' value='" . htmlspecialchars($vhost) . "'>";
|
||||
echo "<button type='submit' onclick='return confirm(\"Disable broken SSL site " . htmlspecialchars($vhost) . " in sites-enabled?\\n\\nThis keeps path fixes while SSL certs are missing.\");'>Disable Broken SSL Vhost: " . htmlspecialchars($vhost) . "</button>";
|
||||
echo "</form>";
|
||||
}
|
||||
echo "<p style='color:#8a6d3b;'>SSL certificate issues are diagnostic only and do not block Panel updates.</p>";
|
||||
}
|
||||
echo "<form method='POST'>\n";
|
||||
echo "<input type='hidden' name='gsp_update_action' value='fix_apache'>\n";
|
||||
|
|
|
|||
|
|
@ -55,11 +55,90 @@ function get_agent_server_status($remote, $server_xml, $home_id, $server_ip, $se
|
|||
$rcon_port = (string)$server_xml->rcon_port;
|
||||
}
|
||||
|
||||
if (!method_exists($remote, 'remote_server_status')) {
|
||||
return array('status' => 'UNKNOWN', 'last_error' => 'Panel remote library does not support agent status.');
|
||||
$agent_status = array(
|
||||
'status' => 'UNKNOWN',
|
||||
'ready' => 0,
|
||||
'process_running' => 0,
|
||||
'session_running' => 0,
|
||||
'game_port_listening' => 0,
|
||||
'query_port_listening' => 0,
|
||||
'rcon_port_listening' => 0,
|
||||
'ip' => $server_ip,
|
||||
'port' => $server_port,
|
||||
'query_port' => $query_port,
|
||||
'rcon_port' => $rcon_port,
|
||||
'last_error' => 'Panel remote library does not support agent status.',
|
||||
);
|
||||
if (method_exists($remote, 'remote_server_status')) {
|
||||
$remote_status = $remote->remote_server_status($home_id, $server_ip, $server_port, $query_port, $rcon_port, $startup_timeout, $state_hint);
|
||||
if (is_array($remote_status)) {
|
||||
$agent_status = array_merge($agent_status, $remote_status);
|
||||
}
|
||||
}
|
||||
|
||||
return $remote->remote_server_status($home_id, $server_ip, $server_port, $query_port, $rcon_port, $startup_timeout, $state_hint);
|
||||
$fallback_session = gsp_agent_screen_running($remote, $home_id);
|
||||
$fallback_port = gsp_agent_port_listening($remote, $server_port);
|
||||
if ($fallback_session === true) {
|
||||
$agent_status['session_running'] = 1;
|
||||
$agent_status['process_running'] = 1;
|
||||
}
|
||||
if ($fallback_port === true) {
|
||||
$agent_status['game_port_listening'] = 1;
|
||||
}
|
||||
if ($fallback_session === true || $fallback_port === true || !empty($agent_status['session_running']) || !empty($agent_status['process_running']) || !empty($agent_status['game_port_listening'])) {
|
||||
$agent_status['status'] = 'ONLINE';
|
||||
$agent_status['ready'] = 1;
|
||||
if (empty($agent_status['last_error']) || $agent_status['last_error'] === 'Agent status RPC unavailable.' || $agent_status['last_error'] === 'Panel remote library does not support agent status.') {
|
||||
$agent_status['last_error'] = '';
|
||||
}
|
||||
$agent_status['status_source'] = ($fallback_port === true) ? 'port' : 'session';
|
||||
return $agent_status;
|
||||
}
|
||||
if (isset($agent_status['status']) && strtoupper((string)$agent_status['status']) === 'UNKNOWN') {
|
||||
if ($fallback_session === false && $fallback_port === false) {
|
||||
$agent_status['status'] = 'OFFLINE';
|
||||
$agent_status['last_error'] = '';
|
||||
}
|
||||
}
|
||||
return $agent_status;
|
||||
}
|
||||
|
||||
function gsp_agent_screen_running($remote, $home_id)
|
||||
{
|
||||
if (!method_exists($remote, 'is_screen_running')) {
|
||||
return null;
|
||||
}
|
||||
$result = $remote->is_screen_running(OGP_SCREEN_TYPE_HOME, $home_id);
|
||||
if ($result === 1) {
|
||||
return true;
|
||||
}
|
||||
if ($result === 0) {
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function gsp_agent_port_listening($remote, $server_port)
|
||||
{
|
||||
$server_port = (int)$server_port;
|
||||
if ($server_port <= 0 || !method_exists($remote, 'exec')) {
|
||||
return null;
|
||||
}
|
||||
$port_arg = escapeshellarg((string)$server_port);
|
||||
$command = 'p='.$port_arg.'; '
|
||||
. 'if command -v ss >/dev/null 2>&1; then ss -lntu 2>/dev/null; else netstat -an 2>/dev/null; fi '
|
||||
. '| tr -d "\r" '
|
||||
. '| grep -Eai "(LISTEN|LISTENING|UDP|tcp|udp)" '
|
||||
. '| grep -E "[:.]$p([[:space:]]|$)" >/dev/null '
|
||||
. '&& echo GSP_PORT_LISTENING || echo GSP_PORT_CLOSED';
|
||||
$output = $remote->exec($command);
|
||||
if (strpos((string)$output, 'GSP_PORT_LISTENING') !== false) {
|
||||
return true;
|
||||
}
|
||||
if (strpos((string)$output, 'GSP_PORT_CLOSED') !== false) {
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function is_agent_status_online($agent_status)
|
||||
|
|
|
|||
|
|
@ -89,31 +89,38 @@ require_once("modules/config_games/server_config_parser.php");
|
|||
{
|
||||
$log_url = "home.php?m=gamemanager&p=log&type=cleared&refreshed&home_id-mod_id-ip-port=".rawurlencode($_GET['home_id-mod_id-ip-port']);
|
||||
echo '<style>
|
||||
#live-server-log {
|
||||
#live-server-log.gsp-live-log-panel {
|
||||
display:block;
|
||||
width:100%;
|
||||
max-width:1600px;
|
||||
min-height:55vh;
|
||||
min-height:500px;
|
||||
height:55vh;
|
||||
max-height:75vh;
|
||||
box-sizing:border-box;
|
||||
font-family:Consolas, Monaco, "Courier New", monospace;
|
||||
font-size:13px;
|
||||
line-height:1.35;
|
||||
margin:0 0 12px 0;
|
||||
padding:12px;
|
||||
background:#050505;
|
||||
color:#e6edf3;
|
||||
border:1px solid #333;
|
||||
border-radius:4px;
|
||||
white-space:pre-wrap;
|
||||
overflow-y:auto;
|
||||
overflow-x:auto;
|
||||
resize:vertical;
|
||||
overflow-wrap:anywhere;
|
||||
tab-size:4;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
#live-server-log {
|
||||
#live-server-log.gsp-live-log-panel {
|
||||
min-height:45vh;
|
||||
height:45vh;
|
||||
font-size:12px;
|
||||
}
|
||||
}
|
||||
</style>';
|
||||
echo '<textarea id="live-server-log" class="log" readonly="readonly" wrap="soft">'.htmlentities($home_log, ENT_QUOTES, "UTF-8").'</textarea>';
|
||||
echo '<pre id="live-server-log" class="gsp-live-log-panel" tabindex="0">'.htmlentities($home_log, ENT_QUOTES, "UTF-8").'</pre>';
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
(function(){
|
||||
|
|
@ -127,7 +134,7 @@ require_once("modules/config_games/server_config_parser.php");
|
|||
fetch('<?php echo $log_url; ?>', {cache: 'no-store'})
|
||||
.then(function(response){ return response.text(); })
|
||||
.then(function(text){
|
||||
logBox.value = text;
|
||||
logBox.textContent = text;
|
||||
if (shouldScroll) {
|
||||
logBox.scrollTop = logBox.scrollHeight;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -546,10 +546,10 @@ echo "<table id='servermonitor' class='tablesorter' data-sortlist='[[0,0],[3,1]]
|
|||
{
|
||||
$status = "unknown";
|
||||
$order = 3;
|
||||
$address = "<span class='note'>Unknown - agent status unavailable.</span>";
|
||||
$address = "<span class='note'>Unknown - status check unavailable.</span>";
|
||||
if(isset($agent_status['last_error']) && $agent_status['last_error'] !== "")
|
||||
$address .= " <span class='failure'>".htmlentities($agent_status['last_error'])."</span>";
|
||||
$offlineT = "<span class='note'>Server state is unknown. The Panel did not confirm that this server is offline.</span>";
|
||||
$offlineT = "<span class='note'>Server state is unknown because the Panel could not complete a reliable agent status check.</span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -120,7 +120,39 @@ function exec_ogp_module()
|
|||
if($log_retval == 1)
|
||||
{
|
||||
$log_url = "home.php?m=gamemanager&p=log&type=cleared&refreshed&home_id-mod_id-ip-port=".rawurlencode($_GET['home_id-mod_id-ip-port']);
|
||||
echo '<textarea id="live-server-log" class="log" readonly="readonly" style="height:500px;overflow:auto;max-width:1600px;width:100%;box-sizing:border-box;">'.htmlentities($home_log, ENT_QUOTES, "UTF-8").'</textarea>';
|
||||
?>
|
||||
<style>
|
||||
.gsp-live-log-panel {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 1600px;
|
||||
min-height: 500px;
|
||||
height: 55vh;
|
||||
box-sizing: border-box;
|
||||
padding: 12px;
|
||||
border: 1px solid #36475f;
|
||||
border-radius: 4px;
|
||||
background: #050a12;
|
||||
color: #d7dde8;
|
||||
font-family: Consolas, Menlo, Monaco, "Courier New", monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.45;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.gsp-live-log-panel {
|
||||
min-height: 45vh;
|
||||
height: 45vh;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
echo '<pre id="live-server-log" class="gsp-live-log-panel" tabindex="0">'.htmlentities($home_log, ENT_QUOTES, "UTF-8").'</pre>';
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
(function(){
|
||||
|
|
@ -134,7 +166,7 @@ function exec_ogp_module()
|
|||
fetch('<?php echo $log_url; ?>', {cache: 'no-store'})
|
||||
.then(function(response){ return response.text(); })
|
||||
.then(function(text){
|
||||
logBox.value = text;
|
||||
logBox.textContent = text;
|
||||
if (shouldScroll) {
|
||||
logBox.scrollTop = logBox.scrollHeight;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,12 @@ The status implementation should check:
|
|||
|
||||
Marker files such as `SERVER_STOPPED` should not be treated as the final source of truth.
|
||||
|
||||
If the configured game port is listening but the managed screen/session is missing, the agent reports `ONLINE` with a warning in `last_error`. This mirrors the Windows agent and prevents a live game process from showing false offline/unknown solely because session tracking failed.
|
||||
|
||||
## Optional Resource Stats Database
|
||||
|
||||
Resource stats database submission uses Perl `DBI`, but `DBI` is optional for normal agent startup. The agent lazy-loads `DBI` only when resource stats are actually submitted. Missing `DBI.pm` should disable database stats submission with a readable log message, not abort the agent at startup.
|
||||
|
||||
## Logging
|
||||
|
||||
Relevant function:
|
||||
|
|
@ -106,4 +112,3 @@ The agent also maintains screen logs and helper scripts inside its runtime area.
|
|||
- It can resolve server ownership and screen users via helper functions such as `find_user_by_screen_id`.
|
||||
- It must remain portable across distro variants, so avoid assuming one exact init system or one exact binary path.
|
||||
- For Windows-targeted games running under Linux, Wine-related path conversion appears in startup path handling.
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,42 @@ The session naming scheme also follows the OGP convention:
|
|||
OGP_HOME_000000123
|
||||
```
|
||||
|
||||
### Windows Startup Launcher
|
||||
|
||||
Relevant files:
|
||||
|
||||
- `Agent-Windows/Install/agent_start.bat`
|
||||
- `Agent-Windows/Install/agent_start_cygwin.sh`
|
||||
- `/OGP/Cfg/bash_prefs.cfg` on an installed node
|
||||
|
||||
The batch launcher must not assume that `bash` is in the Windows `PATH`. It explicitly checks:
|
||||
|
||||
- the Cygwin root beside the launcher
|
||||
- `C:\cygwin64\bin\bash.exe`
|
||||
- `C:\cygwin\bin\bash.exe`
|
||||
|
||||
The Cygwin-side helper performs the shell work:
|
||||
|
||||
1. enter `/OGP`
|
||||
2. normalize CRLF to LF for `.pl`, `.pm`, `.sh`, and `.cfg` files under `/OGP`
|
||||
3. source `/OGP/Cfg/bash_prefs.cfg`
|
||||
4. optionally update only `Agent-Windows/ogp_agent.pl` from Forgejo when `agent_auto_update=1`
|
||||
5. backup the current `/OGP/ogp_agent.pl`
|
||||
6. validate the updated agent with `perl -c`
|
||||
7. restore the backup if validation fails
|
||||
8. launch `/OGP/ogp_agent.pl`
|
||||
|
||||
Default optional update source:
|
||||
|
||||
- repo: `http://forge.runlevelsystems.com/dev/GSP.git`
|
||||
- branch: `Panel-unstable`
|
||||
- source file: `Agent-Windows/ogp_agent.pl`
|
||||
- target file: `/OGP/ogp_agent.pl`
|
||||
|
||||
Auto-update failure is non-fatal. Missing `git`, clone failure, missing source file, or failed validation should warn and continue with the current installed agent.
|
||||
|
||||
The Windows agent file does not use `DBI` at startup. If a Windows node reports `Can't locate DBI.pm` at line 48, it is a strong signal that a Linux agent file was copied onto the Windows node.
|
||||
|
||||
## Status Logic
|
||||
|
||||
Relevant functions:
|
||||
|
|
@ -61,6 +97,8 @@ The status model should check:
|
|||
|
||||
The old `SERVER_STOPPED` file should not be the source of truth.
|
||||
|
||||
If the game port is listening but the managed screen session is not found, the agent reports `ONLINE` with a warning. This protects customers from false offline/unknown states when a process survives a screen/session mismatch or an older agent cannot report the session correctly.
|
||||
|
||||
## Logging
|
||||
|
||||
Relevant function:
|
||||
|
|
@ -91,4 +129,3 @@ The Windows scheduler implementation should remain aligned with the Linux schedu
|
|||
- Batch wrappers are often needed for Windows executables.
|
||||
- Process cleanup must avoid killing unrelated processes that happen to share an executable name.
|
||||
- The agent should continue to use `screen` where it already does so, to stay aligned with Linux behavior.
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ Panel module
|
|||
| `remote_server_status` / `server_status` | Structured server status | `gamemanager` | The preferred source for online/start/stop state. |
|
||||
| `remote_restart_server` / `restart_server` | Restart a server | `gamemanager` | Intended to be stop, wait, start. |
|
||||
| `remote_query` | Query game metadata | `gamemanager` | Optional metadata only. |
|
||||
| `exec` | Execute a constrained agent-side command | `gamemanager` fallback status checks, admin tooling | The Game Monitor currently uses this only as a fallback to test whether the configured game port is listening with `ss`/`netstat`. Customer input must not be passed through this path. |
|
||||
| `steam_cmd` / `steam` / `automatic_steam_update` | SteamCMD-based update flows | Update actions, Workshop/install tools | Used by Steam-based server maintenance. |
|
||||
| `start_file_download` | Download external content | Update/content tools | Returns a PID or progress handle. |
|
||||
| `uncompress_file` | Extract archives | Content installers | Used for zip/tar package installs. |
|
||||
|
|
@ -76,6 +77,25 @@ Return values differ by method, but common patterns are:
|
|||
- `UNRESPONSIVE`
|
||||
- `UNKNOWN`
|
||||
|
||||
The Panel Game Monitor has a defensive fallback path while the structured status system is still being hardened:
|
||||
|
||||
```text
|
||||
remote_server_status if available
|
||||
-> is_screen_running fallback
|
||||
-> agent-side port listening check through exec
|
||||
-> LGSL/GameQ only for optional player/map/query metadata
|
||||
```
|
||||
|
||||
A confirmed managed screen/session, process flag, or listening game port is enough for the Panel to display `ONLINE`. Query failure alone must not produce `OFFLINE` or `UNKNOWN`.
|
||||
|
||||
Current compatibility behavior:
|
||||
|
||||
- New agents should expose `server_status` / `remote_server_status`.
|
||||
- Older agents may only expose `is_screen_running` and generic `exec`.
|
||||
- The Panel therefore falls back to `is_screen_running` and an agent-side port check.
|
||||
- Agents report `ONLINE` when the configured game port is listening even if the managed screen/session cannot be found, with `last_error` carrying the warning.
|
||||
- LGSL/GameQ/Steam query data is optional metadata only.
|
||||
|
||||
## Sequence Diagrams
|
||||
|
||||
### Start
|
||||
|
|
@ -117,7 +137,7 @@ Panel log page
|
|||
Agent
|
||||
-> read screen log or console log
|
||||
Panel
|
||||
-> refresh textarea via AJAX
|
||||
-> refresh dedicated preformatted log panel via AJAX
|
||||
```
|
||||
|
||||
## Error Handling Notes
|
||||
|
|
@ -126,4 +146,3 @@ Panel
|
|||
- Do not treat marker-file presence as the source of truth.
|
||||
- Do not expose raw shell execution to customers through generic RPC routes.
|
||||
- When a method returns a payload, record the error text in the Panel UI if the operation fails.
|
||||
|
||||
|
|
|
|||
|
|
@ -34,11 +34,17 @@ Important references:
|
|||
|
||||
## Log Viewer Layout
|
||||
|
||||
`Panel/modules/gamemanager/log.php` keeps the AJAX refresh behavior and renders the log in a large textarea:
|
||||
`Panel/modules/gamemanager/log.php` and `Panel/modules/gamemanager/view_server_log.php` keep the AJAX refresh behavior and render the log in a large dedicated `<pre>` block:
|
||||
|
||||
- desktop height around `55vh`
|
||||
- element: `#live-server-log.gsp-live-log-panel`
|
||||
- desktop minimum height around `500px`, with `55vh` viewport height
|
||||
- mobile height around `45vh`
|
||||
- monospace font
|
||||
- preserved line breaks
|
||||
- vertical scrolling inside the log panel
|
||||
- long-line overflow remains usable without shrinking the panel to one line
|
||||
- AJAX refresh updates `textContent` only; it does not reload the whole page
|
||||
|
||||
The live viewer intentionally avoids `<textarea>` and the generic `.log` class, because inherited theme styles can make the output appear like a one-line input.
|
||||
|
||||
If a future route shows a one-line log again, inspect the actual rendered element first. The fix should be applied to the route that emits the element, not as a broad theme override.
|
||||
|
|
|
|||
|
|
@ -29,6 +29,23 @@ Panel display mapping:
|
|||
|
||||
LGSL/GameQ query failure is not enough to mark a server offline. Query success may add player/map/hostname metadata, but query failure should only show a small unavailable note when the agent says the server is otherwise active.
|
||||
|
||||
Current repaired Panel flow:
|
||||
|
||||
1. `Panel/modules/gamemanager/server_monitor.php` calls `get_agent_server_status`.
|
||||
2. `get_agent_server_status` first asks for structured `remote_server_status` data when available.
|
||||
3. If the structured response is missing or ambiguous, the Panel uses existing agent calls to check `is_screen_running`.
|
||||
4. The Panel also asks the agent to test the configured game port with `ss` or `netstat`.
|
||||
5. A confirmed managed session, process flag, or listening game port is enough to display `ONLINE`.
|
||||
6. `UNKNOWN` is reserved for an unavailable or inconclusive agent status check.
|
||||
|
||||
The port fallback is deliberately cross-platform:
|
||||
|
||||
- Linux normally uses `ss -lntu`, then `netstat`.
|
||||
- Cygwin/Windows normally uses `netstat -an`.
|
||||
- The Panel-side fallback parses complete command output instead of assuming the listening address is always in the same column.
|
||||
|
||||
Agent behavior now treats a listening game port as `ONLINE` even if the managed screen/session cannot be found. The UI may still show the warning from `last_error`, but it must not show a false red/offline or gray/unknown state when the game port proves the server is running.
|
||||
|
||||
## Recommended State Model
|
||||
|
||||
| State | Meaning |
|
||||
|
|
@ -50,6 +67,8 @@ The agent should check, in this order:
|
|||
4. optional query/RCON port listening
|
||||
5. optional query metadata
|
||||
|
||||
The Panel fallback does not replace a full agent-owned status model. It exists so existing running servers do not show false unknown/offline when the structured status RPC is missing, partial, or query metadata fails.
|
||||
|
||||
## Known Problems To Remember
|
||||
|
||||
- LGSL/GameQ may fail for supported games, blocked ports, or slow startups.
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ The current flow now points toward agent-truth status reporting via:
|
|||
- `remote_server_status`
|
||||
- `get_agent_server_status` in `home_handling_functions.php`
|
||||
- `server_status_without_decrypt` in both agents
|
||||
- fallback `is_screen_running` checks when structured status is unavailable or ambiguous
|
||||
- fallback agent-side `ss`/`netstat` port listening checks through `exec`
|
||||
|
||||
Useful state labels:
|
||||
|
||||
|
|
@ -62,6 +64,21 @@ Useful state labels:
|
|||
|
||||
Query checks should remain optional metadata only.
|
||||
|
||||
Current Panel fallback behavior:
|
||||
|
||||
1. Try the structured `remote_server_status` response.
|
||||
2. Check the managed screen/session with `is_screen_running`.
|
||||
3. Check whether the configured game port is listening from the agent host using `ss` or `netstat`.
|
||||
4. Treat a confirmed session, process, or game port as `ONLINE`.
|
||||
5. Treat query failure as missing metadata only, not as offline or unknown.
|
||||
6. Show `UNKNOWN` only when the Panel cannot complete a reliable agent status check.
|
||||
|
||||
Repair note:
|
||||
|
||||
- `Panel/modules/gamemanager/home_handling_functions.php` contains the status fallback.
|
||||
- The port fallback intentionally parses full `ss`/`netstat` output instead of relying on one fixed field, because Cygwin and Linux output differ.
|
||||
- The Windows and Linux agents report `ONLINE` when the configured game port is listening even if the managed screen/session is missing, with a warning in `last_error`.
|
||||
|
||||
## Game Monitor Display Rules
|
||||
|
||||
`Panel/modules/gamemanager/server_monitor.php` should display state from the agent status response, not from LGSL/GameQ query success alone.
|
||||
|
|
@ -85,7 +102,9 @@ Relevant files:
|
|||
|
||||
The log view should be treated as live, AJAX-updated output rather than a full page reload workflow.
|
||||
|
||||
`log.php` now renders the live log as a large monospace textarea with viewport-based height, preserved line breaks, vertical scrolling, and mobile sizing.
|
||||
`log.php` and `view_server_log.php` now render the live log as a dedicated `<pre id="live-server-log" class="gsp-live-log-panel">` block. They intentionally do not use the generic `log` class or a `textarea`, because theme styles can collapse textarea/log elements into one-line controls.
|
||||
|
||||
The live panel uses viewport-based height, preserved line breaks, vertical scrolling, long-line overflow handling, and mobile sizing.
|
||||
|
||||
## What This Module Depends On
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,12 @@ Defaults:
|
|||
- Panel Path: `/var/www/html/GSP/Panel`
|
||||
- Backup Before Update: enabled
|
||||
|
||||
Important implementation note:
|
||||
|
||||
- `gsp_update_settings()` and `gsp_validate_update_settings()` are defined at top level in `Panel/modules/administration/panel_update.php`.
|
||||
- These helpers must not be nested inside another function. A previous bad edit placed `gsp_update_settings()` inside `gsp_get_git_commit()`, which caused a fatal error when the update page called the helper before `gsp_get_git_commit()` had ever executed.
|
||||
- If the update page throws `Call to undefined function gsp_update_settings()`, first verify the deployed `Panel/modules/administration/panel_update.php` matches the repository version and that this helper exists near the top of the file before `gsp_panel_update_section()` is called.
|
||||
|
||||
## Update Flow
|
||||
|
||||
1. Save or submit repository settings.
|
||||
|
|
@ -52,7 +58,7 @@ Defaults:
|
|||
|
||||
Apache and SSL checks are diagnostics only. Missing SSL certificates do not block Panel updates.
|
||||
|
||||
The old repeated SSL vhost disable buttons are not part of the primary update page. Apache path repair remains available under Advanced Diagnostics.
|
||||
The old repeated SSL vhost disable buttons are not part of the primary update page. Apache path repair remains available under Advanced Diagnostics, and SSL issues are shown as concise diagnostic warnings.
|
||||
|
||||
## Remaining Issues
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue