From 3d93d01cd1c5f8bd314bdc9cd35c113c6a7c7a15 Mon Sep 17 00:00:00 2001 From: Frank Harris Date: Sat, 20 Jun 2026 21:19:11 -0500 Subject: [PATCH] fixed installer --- Cfg/Config.pm | 37 +- Cfg/Config.pm.default | 20 + Cfg/Preferences.pm | 5 + Cfg/Preferences.pm.default | 14 + Cfg/bash_prefs.cfg.default | 8 + DEVELOPMENT | 3 +- EHCP/addAccount.php | 4 +- EHCP/config.php | 4 +- README.md | 69 ++- agent_conf.sh | 896 +++++++-------------------------- db/php_aggregator.php | 4 +- docs/AGENT_ACTIVITY_EVENTS.md | 8 +- docs/PANEL_INTEGRATION.md | 22 + documentation/agent-guide.md | 222 +++++--- gsp_agent_run | 60 +++ includes/ogp_agent.init | 10 +- includes/ogp_agent.init.dbn | 16 +- includes/ogp_agent.init.gentoo | 13 +- includes/ogp_agent.init.rh | 28 +- install.sh | 496 +++++++----------- install_agent_prereqs.sh | 132 +++-- ogp_agent.pl | 131 ++++- ogp_agent_run | 414 +-------------- ogp_screenrc | 3 +- resource_usage_test.pl | 2 +- systemd/gsp_agent.service | 20 + systemd/ogp_agent.service | 20 +- 27 files changed, 996 insertions(+), 1665 deletions(-) create mode 100644 Cfg/Config.pm.default create mode 100644 Cfg/Preferences.pm.default create mode 100644 Cfg/bash_prefs.cfg.default mode change 100644 => 100755 agent_conf.sh create mode 100755 gsp_agent_run mode change 100644 => 100755 install.sh mode change 100644 => 100755 install_agent_prereqs.sh mode change 100644 => 100755 ogp_agent_run create mode 100644 systemd/gsp_agent.service diff --git a/Cfg/Config.pm b/Cfg/Config.pm index d7c8337..4feee77 100644 --- a/Cfg/Config.pm +++ b/Cfg/Config.pm @@ -1,19 +1,20 @@ %Cfg::Config = ( - logfile => '/home/gameserver/OGP/ogp_agent.log', - listen_port => '12679', - listen_ip => '0.0.0.0', - version => 'v1.4', - key => 'Mvemjsu9p', - steam_license => 'Accept', - sudo_password => 'Inc0rrect!', - web_admin_api_key => '{your_admin_ogp_web_api_key_here}', - web_api_url => '{your_url_to_ogp_api.php}', - steam_dl_limit => '0', - # Resource stats database configuration - stats_db_host => 'mysql.iaregamer.com', - stats_db_user => 'remoteuser', - stats_db_pass => 'Pkloyn7yvpht!', - stats_db_name => 'panel', - stats_table_prefix => 'gsp_', - stats_frequency_minutes => '5', - ); \ No newline at end of file + logfile => '/home/gameserver/GSP/gsp_agent.log', + listen_port => '12679', + listen_ip => '0.0.0.0', + version => 'v1.4', + key => 'CHANGE_ME_PANEL_AGENT_KEY', + steam_license => 'Accept', + sudo_password => '', + web_admin_api_key => '', + web_api_url => '', + agent_event_url => '', + remote_server_id => '', + steam_dl_limit => '0', + stats_db_host => '', + stats_db_user => '', + stats_db_pass => '', + stats_db_name => '', + stats_table_prefix => 'gsp_', + stats_frequency_minutes => '5', +); diff --git a/Cfg/Config.pm.default b/Cfg/Config.pm.default new file mode 100644 index 0000000..4feee77 --- /dev/null +++ b/Cfg/Config.pm.default @@ -0,0 +1,20 @@ +%Cfg::Config = ( + logfile => '/home/gameserver/GSP/gsp_agent.log', + listen_port => '12679', + listen_ip => '0.0.0.0', + version => 'v1.4', + key => 'CHANGE_ME_PANEL_AGENT_KEY', + steam_license => 'Accept', + sudo_password => '', + web_admin_api_key => '', + web_api_url => '', + agent_event_url => '', + remote_server_id => '', + steam_dl_limit => '0', + stats_db_host => '', + stats_db_user => '', + stats_db_pass => '', + stats_db_name => '', + stats_table_prefix => 'gsp_', + stats_frequency_minutes => '5', +); diff --git a/Cfg/Preferences.pm b/Cfg/Preferences.pm index 1e76091..a85cf79 100644 --- a/Cfg/Preferences.pm +++ b/Cfg/Preferences.pm @@ -1,9 +1,14 @@ %Cfg::Preferences = ( screen_log_local => '1', delete_logs_after => '30', + gsp_manages_ftp => '1', ogp_manages_ftp => '1', ftp_method => 'PureFTPd', + gsp_autorestart_server => '1', ogp_autorestart_server => '1', protocol_shutdown_waittime => '10', linux_user_per_game_server => '1', + PortValidationEnabled => '1', + StartupValidationTimeoutSeconds => '180', + PortCheckIntervalSeconds => '5', ); diff --git a/Cfg/Preferences.pm.default b/Cfg/Preferences.pm.default new file mode 100644 index 0000000..a85cf79 --- /dev/null +++ b/Cfg/Preferences.pm.default @@ -0,0 +1,14 @@ +%Cfg::Preferences = ( + screen_log_local => '1', + delete_logs_after => '30', + gsp_manages_ftp => '1', + ogp_manages_ftp => '1', + ftp_method => 'PureFTPd', + gsp_autorestart_server => '1', + ogp_autorestart_server => '1', + protocol_shutdown_waittime => '10', + linux_user_per_game_server => '1', + PortValidationEnabled => '1', + StartupValidationTimeoutSeconds => '180', + PortCheckIntervalSeconds => '5', +); diff --git a/Cfg/bash_prefs.cfg.default b/Cfg/bash_prefs.cfg.default new file mode 100644 index 0000000..104546a --- /dev/null +++ b/Cfg/bash_prefs.cfg.default @@ -0,0 +1,8 @@ +agent_auto_update=0 +agent_update_repo_url=http://forge.runlevelsystems.com/dev/GSP.git +agent_update_branch=Panel-unstable +agent_update_raw_url=http://forge.runlevelsystems.com/dev/GSP/raw/branch/Panel-unstable/Agent-Linux/ogp_agent.pl +run_pureftpd=1 +ftp_port=21 +ftp_ip=0.0.0.0 +ftp_pasv_range= diff --git a/DEVELOPMENT b/DEVELOPMENT index 0be2731..aa7ee42 100644 --- a/DEVELOPMENT +++ b/DEVELOPMENT @@ -1,6 +1,5 @@ -OGP Agent NOTES: +GSP Agent NOTES: Before committing code it is recommended to execute perltidy: $ perltidy -b -gnu ogp_agent.pl - diff --git a/EHCP/addAccount.php b/EHCP/addAccount.php index 12261e8..f542c6f 100644 --- a/EHCP/addAccount.php +++ b/EHCP/addAccount.php @@ -27,7 +27,7 @@ if (file_exists("config.php")) { include_once 'db_functions.php'; -// Did we properly receive the variables from the OGP agent? +// Did we properly receive the variables from the GSP agent? if (isset($ftp_username) && isset($ftp_pass) && isset($rDir)) { @@ -35,7 +35,7 @@ if (isset($ftp_username) && isset($ftp_pass) && isset($rDir)) { $errorCount = 0; $errorInstallInt = 0; - // OGP should be doing this validation... but it's not + // GSP should be doing this validation before calling this helper. // Custom directory validation diff --git a/EHCP/config.php b/EHCP/config.php index b4519a2..99981c2 100644 --- a/EHCP/config.php +++ b/EHCP/config.php @@ -1,7 +1,7 @@ &2 - exit 1 -fi +Usage: + ./agent_conf.sh --guided + ./agent_conf.sh [options] -usage() -{ -cat << EOF +Options: + --install-dir PATH Agent install directory. Default: directory containing this script. + --agent-user USER Owner for generated config files. + --key VALUE Agent key/shared secret from the GSP Panel remote-server record. + --listen-ip IP Agent bind IP. Default: ${DEFAULT_IP} + --listen-port PORT Agent bind port. Default: ${DEFAULT_PORT} + --sudo-password VALUE Optional sudo password for legacy sudo_exec flows. + --web-api-url URL Panel ogp_api.php URL. + --agent-event-url URL Panel agent_event_receiver.php URL. + --remote-server-id ID Panel remote server ID. + --ftp-managed 0|1 Whether GSP should manage FTP accounts. Default: 1 + --ftp-method NAME FTP backend. Default: ${DEFAULT_FTP_METHOD} + --auto-restart 0|1 Restart crashed game servers. Default: 1 + --auto-update 0|1 Restart-time agent auto update. Default: 0 + --guided Prompt for missing values. + --help Show this help. -Usage: $0 option +Legacy aliases: + -s VALUE maps to --sudo-password + -u USER maps to --agent-user -OPTIONS: - -s password Set the password for the agent's user (Linux) - -p password Set the password for cyg_server user (Windows) - -u ogpuser Set the username of the ogp user +This script writes: + Cfg/Config.pm + Cfg/Preferences.pm + Cfg/bash_prefs.cfg EOF } -while getopts "hs:p:u" OPTION -do - case $OPTION in - s) - sudo_password=$OPTARG - ;; - p) - cs_psw=$OPTARG - ;; - u) - agent_user=$OPTARG - ;; - - ?) - exit - ;; - esac -done +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +install_dir="$script_dir" +agent_user="" +guided=0 +key="" +listen_ip="$DEFAULT_IP" +listen_port="$DEFAULT_PORT" +sudo_password="" +web_api_url="" +agent_event_url="" +remote_server_id="" +steam_license="Accept" +ftp_managed="1" +ftp_method="$DEFAULT_FTP_METHOD" +auto_restart="1" +auto_update="0" +screen_log_local="1" +delete_logs_after="30" +linux_user_per_game_server="1" +protocol_shutdown_waittime="10" +ftp_port="$DEFAULT_FTP_PORT" +ftp_ip="$DEFAULT_IP" +ftp_pasv_range="$DEFAULT_FTP_PASV_RANGE" -if [ -z $1 ] -then - usage - exit -fi - -if [ "$(uname -o)" == "Cygwin" ]; then - if [ -z $cs_psw ] - then - echo "Must use -p argument instead of -s." - exit - fi -else - if [ -z $sudo_password ] - then - echo "Must use -s argument instead of -p." - exit - fi -fi - -readonly DEFAULT_PORT=12679 -readonly DEFAULT_IP=0.0.0.0 -readonly DEFAULT_FTP_PORT=21 -readonly DEFAULT_FTP_PASV_RANGE=40000:50000 -readonly AGENT_VERSION='v1.4' - -failed() -{ - echo "ERROR: ${1}" - exit 1 +ask() { + local prompt="$1" + local default_value="$2" + local answer + read -r -p "${prompt} [${default_value}]: " answer + printf '%s' "${answer:-$default_value}" } -agent_home="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ask_secret() { + local prompt="$1" + local answer + read -r -s -p "${prompt}: " answer + echo + printf '%s' "$answer" +} -cfgfile=${agent_home}/Cfg/Config.pm -prefsfile=${agent_home}/Cfg/Preferences.pm -bashprefsfile=${agent_home}/Cfg/bash_prefs.cfg +require_01() { + local name="$1" + local value="$2" + [[ "$value" =~ ^[01]$ ]] || { + echo "ERROR: ${name} must be 0 or 1." >&2 + exit 1 + } +} -overwrite_config=1 -if [ -e ${cfgfile} ]; then - while [ 1 ] - do - echo "Overwrite old configuration?" - echo -n "(yes/no) [Default yes]: " - read octmp - if [ "$octmp" == "yes" -o -z "$octmp" ] - then - break - elif [ "$octmp" == "no" ] - then - overwrite_config=0 - break - else - echo "You need to type 'yes', 'no' or leave empty for default value [yes]." - fi - done +while [ "$#" -gt 0 ]; do + case "$1" in + --install-dir) install_dir="${2:-}"; shift 2 ;; + --agent-user) agent_user="${2:-}"; shift 2 ;; + --key) key="${2:-}"; shift 2 ;; + --listen-ip) listen_ip="${2:-}"; shift 2 ;; + --listen-port) listen_port="${2:-}"; shift 2 ;; + --sudo-password) sudo_password="${2:-}"; shift 2 ;; + --web-api-url) web_api_url="${2:-}"; shift 2 ;; + --agent-event-url) agent_event_url="${2:-}"; shift 2 ;; + --remote-server-id) remote_server_id="${2:-}"; shift 2 ;; + --ftp-managed) ftp_managed="${2:-}"; shift 2 ;; + --ftp-method) ftp_method="${2:-}"; shift 2 ;; + --auto-restart) auto_restart="${2:-}"; shift 2 ;; + --auto-update) auto_update="${2:-}"; shift 2 ;; + --guided) guided=1; shift ;; + -s) sudo_password="${2:-}"; shift 2 ;; + -u) agent_user="${2:-}"; shift 2 ;; + --help|-h) usage; exit 0 ;; + *) echo "ERROR: unknown option: $1" >&2; usage; exit 1 ;; + esac +done + +install_dir="${install_dir%/}" +cfg_dir="${install_dir}/Cfg" +cfgfile="${cfg_dir}/Config.pm" +prefsfile="${cfg_dir}/Preferences.pm" +bashprefsfile="${cfg_dir}/bash_prefs.cfg" + +if [ "$guided" -eq 1 ]; then + echo "GSP Linux Agent configuration" + echo "Use values from Panel -> Administration -> Game Servers for this remote server." + echo + key="${key:-$(ask_secret "Agent key/shared secret")}" + listen_ip="$(ask "Listen IP" "$listen_ip")" + listen_port="$(ask "Listen port" "$listen_port")" + web_api_url="$(ask "Panel API URL, usually https://panel.example.com/ogp_api.php" "$web_api_url")" + agent_event_url="$(ask "Panel event receiver URL, usually https://panel.example.com/agent_event_receiver.php" "$agent_event_url")" + remote_server_id="$(ask "Panel remote server ID" "$remote_server_id")" + ftp_managed="$(ask "Manage FTP accounts? 1=yes, 0=no" "$ftp_managed")" + ftp_method="$(ask "FTP method" "$ftp_method")" + auto_restart="$(ask "Restart crashed game servers? 1=yes, 0=no" "$auto_restart")" + auto_update="$(ask "Restart-time agent auto-update? 1=yes, 0=no" "$auto_update")" + if [ -z "$sudo_password" ]; then + sudo_password="$(ask_secret "Optional sudo password for legacy sudo_exec flows, leave blank if not used")" + fi fi -if [ "X${overwrite_config}" == "X1" ] -then - if [ -z "$sudo_password" ]; then - if [ -f "$cfgfile" ]; then - sudo_password=`awk '/sudo_password/{print $3}' $cfgfile|sed -e "s#\('\)\(.*\)\(',\)#\2#"` - fi - fi - echo "#######################################################################" - echo "" - echo "OGP agent uses basic encryption to prevent unauthorized users from connecting" - echo "Enter a string of alpha-numeric characters for example 'abcd12345'" - echo "**** NOTE - Use the same key in your Open Game Panel webpage config file - they must match *****" - echo "" +[ -n "$key" ] || key="CHANGE_ME_PANEL_AGENT_KEY" +[[ "$listen_port" =~ ^[0-9]+$ ]] || { + echo "ERROR: listen port must be numeric." >&2 + exit 1 +} +require_01 "ftp-managed" "$ftp_managed" +require_01 "auto-restart" "$auto_restart" +require_01 "auto-update" "$auto_update" - while [ -z "${key}" ] - do - echo -n "Set encryption key: " - read key - done +mkdir -p "$cfg_dir" "${install_dir}/logs" "${install_dir}/tmp" "${install_dir}/screenlogs" "${install_dir}/_gsp_content/hooks" "${install_dir}/_gsp_content/generated" "${install_dir}/_gsp_content/runtime" - echo - echo "Set the listen port for the agent. The default should be fine for everyone." - echo "However, if you want to change it that can be done here, otherwise just press Enter." - echo -n "Set listen port [Default ${DEFAULT_PORT}]: " - read port - - if [ -z "${port}" ] - then - port=$DEFAULT_PORT - fi - - echo - echo "Set the listen IP for the agent." - echo "Use ${DEFAULT_IP} to bind on all interfaces." - echo -n "Set listen IP [Default ${DEFAULT_IP}]: " - read ip - - if [ -z "${ip}" ] - then - ip=$DEFAULT_IP - fi - - while [ 1 ] - do - echo - echo "For some games the OGP panel is using Steam client." - echo "This client has its own license that you need to agree before continuing." - echo "This agreement is available at http://store.steampowered.com/subscriber_agreement/" - echo; - echo "Do you accept the terms of Steam(tm) Subscriber Agreement?" - echo -n "(Accept|Reject): " - read steam_license - - if [ "$steam_license" == "Accept" -o "$steam_license" == "Reject" ] - then - break; - fi - - echo "You need to type either 'Accept' or 'Reject'." - done - - echo "Writing Config file - $cfgfile" - - - # Prompt for resource stats MySQL settings - echo - echo "Configure Resource Stats MySQL Database Connection:" - echo -n "Stats DB Host [Default 127.0.0.1]: " - read stats_db_host - if [ -z "$stats_db_host" ]; then stats_db_host='127.0.0.1'; fi - echo -n "Stats DB User [Default panel_user]: " - read stats_db_user - if [ -z "$stats_db_user" ]; then stats_db_user='panel_user'; fi - echo -n "Stats DB Password [Default REPLACE_ME]: " - read stats_db_pass - if [ -z "$stats_db_pass" ]; then stats_db_pass='REPLACE_ME'; fi - echo -n "Stats DB Name [Default panel_database]: " - read stats_db_name - if [ -z "$stats_db_name" ]; then stats_db_name='panel_database'; fi - echo -n "Stats Table Prefix [Default gsp_]: " - read stats_table_prefix - if [ -z "$stats_table_prefix" ]; then stats_table_prefix='gsp_'; fi - echo -n "Stats Frequency Minutes [Default 5]: " - read stats_frequency_minutes - if [ -z "$stats_frequency_minutes" ]; then stats_frequency_minutes='5'; fi - - echo "%Cfg::Config = ( - logfile => '${agent_home}/ogp_agent.log', - listen_port => '${port}', - listen_ip => '${ip}', - version => '${AGENT_VERSION}', +cat > "$cfgfile" < '${install_dir}/gsp_agent.log', + listen_port => '${listen_port}', + listen_ip => '${listen_ip}', + version => '${DEFAULT_AGENT_VERSION}', key => '${key}', steam_license => '${steam_license}', sudo_password => '${sudo_password}', - web_admin_api_key => '{your_admin_ogp_web_api_key_here}', - web_api_url => '{your_url_to_ogp_api.php}', - agent_event_url => '{your_url_to_agent_event_receiver.php}', - remote_server_id => '{panel_remote_server_id}', + web_admin_api_key => '', + web_api_url => '${web_api_url}', + agent_event_url => '${agent_event_url}', + remote_server_id => '${remote_server_id}', steam_dl_limit => '0', - # Resource stats database configuration - stats_db_host => '${stats_db_host}', - stats_db_user => '${stats_db_user}', - stats_db_pass => '${stats_db_pass}', - stats_db_name => '${stats_db_name}', - stats_table_prefix => '${stats_table_prefix}', - stats_frequency_minutes => '${stats_frequency_minutes}', - );" > $cfgfile - - if [ $? != 0 ] - then - failed "Failed to write config file." - else - chmod 600 ${cfgfile} || failed "Failed to chmod ${cfgfile} to 600." - fi + stats_db_host => '', + stats_db_user => '', + stats_db_pass => '', + stats_db_name => '', + stats_table_prefix => 'gsp_', + stats_frequency_minutes => '5', +); +EOF - echo; - while [ 1 ] - do - echo "The agent should be updated when the service is restarted or started?" - echo -n "(yes|no) [Default yes]: " - read auto_update - if [ "${auto_update}" == "yes" -o "${auto_update}" == "no" -o -z "${auto_update}" ] - then - if [ "${auto_update}" == "yes" ] - then - autoUpdate=1 - elif [ -z "${auto_update}" ] - then - autoUpdate=1 - else - autoUpdate=0 - fi - break; - fi - echo "You need to type 'yes', 'no' or leave empty for default value [yes]." - done +cat > "$prefsfile" < '${screen_log_local}', + delete_logs_after => '${delete_logs_after}', + gsp_manages_ftp => '${ftp_managed}', + ogp_manages_ftp => '${ftp_managed}', + ftp_method => '${ftp_method}', + gsp_autorestart_server => '${auto_restart}', + ogp_autorestart_server => '${auto_restart}', + protocol_shutdown_waittime => '${protocol_shutdown_waittime}', + linux_user_per_game_server => '${linux_user_per_game_server}', + PortValidationEnabled => '1', + StartupValidationTimeoutSeconds => '180', + PortCheckIntervalSeconds => '5', +); +EOF - echo; - while [ 1 ] - do - echo "The agent should backup the server log files in the game server directory?" - echo -n "(yes|no) [Default yes]: " - read log_local_copy - if [ "${log_local_copy}" == "yes" -o "${log_local_copy}" == "no" -o -z "${log_local_copy}" ] - then - if [ "${log_local_copy}" == "yes" ] - then - logLocalCopy=1 - elif [ -z "${log_local_copy}" ] - then - logLocalCopy=1 - else - logLocalCopy=0 - fi - break; - fi - echo "You need to type 'yes', 'no' or leave empty for default value [yes]." - done +cat > "$bashprefsfile" < ${agent_home}/IspConfig/soap_config.php - echo "\$password = '${remote_login_password}';" >> ${agent_home}/IspConfig/soap_config.php - echo "\$soap_location = 'http${secure}://127.0.0.1:${port}/remote/index.php';" >> ${agent_home}/IspConfig/soap_config.php - echo -e "\$soap_uri = 'http${secure}://127.0.0.1:${port}/remote/';\n?>" >> ${agent_home}/IspConfig/soap_config.php - - else - while [ 1 ] - do - echo; - echo "If you have installed the Easy Hosting Control Panel (EHCP - www.ehcpforce.tk)," - echo "the agent can use it to create FTP accounts instead of using Pure-FTPd." - echo "Would you like to configure this agent to use the API of EHCP?" - echo -n "(yes|no) [Default no]: " - read ehcp - if [ "${ehcp}" == "yes" -o "${ehcp}" == "no" -o -z "${ehcp}" ] - then - if [ "${ehcp}" == "yes" ] - then - ftpMethod="EHCP" - fi - break; - fi - echo "You need to type 'yes', 'no' or leave empty for default value [no]." - done - - if [ "${ehcp}" == "yes" ] - then - echo "Please enter the MySQL database password for the ehcp user" - echo -n "(created during the install of EHCP): " - read ehcpDB - - ehcpConf=${agent_home}/EHCP/config.php - sed -i "s/changeme/${ehcpDB}/" $ehcpConf - else - while [ 1 ] - do - echo; - echo "The agent can use ProFTPd to create FTP accounts." - echo "Would you like to configure this agent to use the ProFTPd?" - echo -n "(yes|no) [Default no]: " - read proftpd - if [ "${proftpd}" == "yes" -o "${proftpd}" == "no" -o -z "${proftpd}" ] - then - if [ "${proftpd}" == "yes" ] - then - ftpMethod="proftpd" - fi - break; - fi - echo "You need to type 'yes', 'no' or leave empty for default value [no]." - done - - if [ "${proftpd}" == "yes" ] - then - echo "Please enter the path for proFTPd configuration file" - echo -n "[Default /etc/proftpd/proftpd.conf]: " - read proFTPdConfFile - if [ -z $proFTPdConfFile ] - then - proFTPdConfFile="/etc/proftpd/proftpd.conf" - if [ ! -e "$proFTPdConfFile" ]; then - proFTPdConfFile="/etc/proftpd.conf" - fi - fi - proFTPdConfPath=$(dirname ${proFTPdConfFile}) - while [ 1 ] - do - if [ ! -e $proFTPdConfFile ] - then - echo "The file ${proFTPdConfFile} does not exists," - echo "what you want to do, reenter it or ignore and continue?" - echo "If your answer is 'ignore' is meant that you will install proFTPd later." - echo -n "(reenter|ignore): " - read answer - if [ "${answer}" == "reenter" -o "${answer}" == "ignore" ] - then - if [ "${answer}" == "reenter" ] - then - echo "Reenter proFTPd's configuration file path:" - read proFTPdConfFile - continue - elif [ "${answer}" == "ignore" ] - then - echo "You will need to append this to ${proFTPdConfFile} once you've installed proftpd:" - bold=`tput bold` - normal=`tput sgr0` - echo -e "\n\n${bold}RequireValidShell off\nAuthUserFile ${proFTPdConfPath}/ftpd.passwd\nAuthGroupFile ${proFTPdConfPath}/ftpd.group${normal}\n\n" - break - fi - fi - echo "You need to type 'reenter' or 'ignore'." - else - if egrep -iq "LoadModule\s*mod_auth_file.c" ${proFTPdConfFile} - then - sed -i "s/\s*#\s*LoadModule\s*mod_auth_file.c/LoadModule mod_auth_file.c/g" ${proFTPdConfFile} - else - echo -e "LoadModule mod_auth_file.c" >> ${proFTPdConfFile} - fi - - if egrep -iq "^\s*AuthOrder.*" ${proFTPdConfFile} - then - if egrep -iq "^\s*AuthOrder.*mod_auth_file.c" ${proFTPdConfFile} - then - false - else - sed -ri "s/(^\s*AuthOrder.*)/\1 mod_auth_file.c/g" ${proFTPdConfFile} - fi - else - echo -e "AuthOrder mod_auth_file.c" >> ${proFTPdConfFile} - fi - - if egrep -iq "RequireValidShell.*" ${proFTPdConfFile} - then - sed -i "s#RequireValidShell.*#RequireValidShell off#g" ${proFTPdConfFile} - else - echo -e "RequireValidShell off" >> ${proFTPdConfFile} - fi - - if egrep -iq "AuthUserFile.*" ${proFTPdConfFile} - then - sed -i "s#AuthUserFile.*#AuthUserFile ${proFTPdConfPath}/ftpd.passwd#g" ${proFTPdConfFile} - else - echo -e "AuthUserFile "${proFTPdConfPath}"/ftpd.passwd" >> ${proFTPdConfFile} - fi - - if egrep -iq "AuthGroupFile.*" ${proFTPdConfFile} - then - sed -i "s#AuthGroupFile.*#AuthGroupFile ${proFTPdConfPath}/ftpd.group#g" ${proFTPdConfFile} - else - echo -e "AuthGroupFile "${proFTPdConfPath}"/ftpd.group" >> ${proFTPdConfFile} - fi - - # Allow global overwrite (http://opengamepanel.org/forum/viewthread.php?thread_id=5202) - if egrep -iq "AllowOverwrite.*" ${proFTPdConfFile} - then - sed -i "s#AllowOverwrite.*#AllowOverwrite yes#g" ${proFTPdConfFile} - else - echo -e "\nAllowOverwrite yes\n" >> ${proFTPdConfFile} - fi - - if [ ! -e "${proFTPdConfPath}/ftpd.group" ] - then - touch ${proFTPdConfPath}/ftpd.group - fi - - if [ ! -e "${proFTPdConfPath}/ftpd.passwd" ] - then - touch ${proFTPdConfPath}/ftpd.passwd - fi - ftpd_user=$(grep -oP '^User\s+\K.+' ${proFTPdConfFile}) - ftpd_group=$(grep -oP '^Group\s+\K.+' ${proFTPdConfFile}) - if [ -z "$agent_user" ]; then - agent_user=$(grep -oP 'agent_user=\K.+' /etc/init.d/ogp_agent) - fi - if [ ! -z "$ftpd_user" ] && [ ! -z "$ftpd_group" ] && [ ! -z "$agent_user" ] - then - if [ "$(groups $agent_user|grep $ftpd_group)" == "" ] - then - usermod -aG $ftpd_group $agent_user - fi - if [ -e "${proFTPdConfPath}/ftpd.passwd" -a -e "${proFTPdConfPath}/ftpd.group" ] - then - chmod 640 ${proFTPdConfPath}/ftpd.* - chown -f $ftpd_user:$ftpd_group ${proFTPdConfPath}/ftpd.* - fi - fi - break - fi - done - - if [ -e "/etc/init.d/proftpd" ] - then - /etc/init.d/proftpd restart - else - echo "If proftpd is running, to apply the changes, you must restart the service." - fi - else - ftpMethod="PureFTPd" - fi - fi - fi - else - if uname -a|grep -q "x86_64" - then - FZ="yes" - else - while [ 1 ] - do - echo; - echo "If you have installed the FileZilla Server," - echo "the agent can use it to create FTP accounts instead of using Pure-FTPd." - echo "Would you like to configure this agent to use it?" - echo -n "(yes|no) [Default no]: " - read FZ - if [ "${FZ}" == "yes" -o "${FZ}" == "no" -o -z "${FZ}" ] - then - break; - fi - echo "You need to type 'yes', 'no' or leave empty for default value [no]."; - done - fi - - if [ "${FZ}" == "yes" ]; then - ftpMethod="FZ" - PF=$(cmd /Q /C echo %PROGRAMFILES\(X86\)% | sed 's/\r$//') - if [ "X${PF}" == "X" ];then PF=$(cmd /Q /C echo %PROGRAMFILES% | sed 's/\r$//'); fi - echo; - echo "Please enter the path for FileZilla server executable file" - echo -n "[Default ${PF}\\FileZilla Server\\FileZilla server.exe]: " - read -r FZ_EXE - if [ -z "${FZ_EXE}" ] - then - FZ_EXE="${PF}\\FileZilla Server\\FileZilla server.exe" - fi - echo; - echo "Please enter the path for FileZilla server xml file" - echo -n "[Default ${PF}\\FileZilla Server\\FileZilla Server.xml]: " - read -r FZ_XML - if [ -z "${FZ_XML}" ] - then - FZ_XML="${PF}\\FileZilla Server\\FileZilla server.xml" - fi - FZ_EXE=$(cygpath -u "$FZ_EXE") - FZ_XML=$(cygpath -u "$FZ_XML") - FZconf=${agent_home}/Cfg/FileZilla.pm - echo -e "%Cfg::FileZilla = (\n\tfz_exe => '${FZ_EXE}',\n\tfz_xml => '${FZ_XML}'\n);" > ${FZconf} - if [ $? != 0 ] - then - failed "Failed to write FileZilla configuration file." - fi - if [ ! -z "$cs_psw" ]; then - UD=$(cmd /Q /C echo %USERDOMAIN% | sed 's/\r$//') - net stop "FileZilla Server" - sc config "FileZilla Server" obj= "${UD}\cyg_server" password= "$cs_psw" type= own - net start "FileZilla Server" - fi - else - if uname -a|grep -qv "x86_64" - then - ftpMethod="PureFTPd" - echo; - echo "Set the listen IP for the PureFTPd." - echo "Default is (${DEFAULT_IP}) to bind on all interfaces." - echo -n "Set listen IP [Default ${DEFAULT_IP}]: " - read ftp_ip - - if [ -z "${ftp_ip}" ] - then - ftp_ip=$DEFAULT_IP - fi - - echo - echo "Set the listen port for PureFTPd. The default should be fine for everyone." - echo "However, if you want to change it that can be done here, otherwise just press Enter." - echo -n "Set listen port [Default ${DEFAULT_FTP_PORT}]: " - read port - - if [ -z "${ftp_port}" ] - then - ftp_port=$DEFAULT_FTP_PORT - fi - - echo - echo "Passive-mode downloads." - echo "This is especially useful if the server is behind a firewall." - echo -n "Use only ports in the range?(yes|no)[Default no]: " - read passive_ftp - - if [ -z "${passive_ftp}" -o "${passive_ftp}" != "yes" ] - then - ftp_pasv_range="" - else - echo "Enter passive ports range separated by colon (:)." - echo -n "[Default ${DEFAULT_FTP_PASV_RANGE}]: " - read ftp_pasv_range - if [ -z "${ftp_pasv_range}" ] - then - ftp_pasv_range=$DEFAULT_FTP_PASV_RANGE - fi - fi - fi - fi - fi - fi - - if [ "${ftpMethod}" == "PureFTPd" ] - then - run_pureftpd=1 - else - run_pureftpd=0 - fi - - echo "Writing Preferences file - $prefsfile" - - prefs="%Cfg::Preferences = (\n" - prefs="${prefs}\tscreen_log_local => '${logLocalCopy}',\n" - prefs="${prefs}\tdelete_logs_after => '${deleteLogsAfter}',\n" - prefs="${prefs}\togp_manages_ftp => '${ogpManagesFTP}',\n" - prefs="${prefs}\tftp_method => '${ftpMethod}',\n" - prefs="${prefs}\togp_autorestart_server => '${autoRestart}',\n" - prefs="${prefs}\tprotocol_shutdown_waittime => '10',\n" - prefs="${prefs}\tlinux_user_per_game_server => '1',\n" - if [ "X${proftpd}" == "Xyes" ] - then - prefs="${prefs}\tproftpd_conf_path => '${proFTPdConfPath}',\n" - fi - prefs="${prefs});" - echo -e $prefs > $prefsfile - - if [ $? != 0 ] - then - failed "Failed to write preferences file." - fi - - echo "Writing bash script preferences file - $bashprefsfile" - - echo -e "agent_auto_update=${autoUpdate}\nrun_pureftpd=${run_pureftpd}\nftp_port=${ftp_port}\nftp_ip=${ftp_ip}\nftp_pasv_range=${ftp_pasv_range}" > $bashprefsfile - - if [ $? != 0 ] - then - failed "Failed to write MISC configuration file used by bash scripts." - fi - - # Ensure all config files are owned by the installation owner - if [ -n "$agent_user" ]; then - # Ensure agent_user is valid and not root - if id "$agent_user" >/dev/null 2>&1 && [ "$agent_user" != "root" ]; then - chown "$agent_user" "$cfgfile" "$prefsfile" "$bashprefsfile" 2>/dev/null - fi - fi +if [ -n "$agent_user" ] && [ "$(id -u)" -eq 0 ] && id "$agent_user" >/dev/null 2>&1; then + chown "$agent_user:$agent_user" "$cfgfile" "$prefsfile" "$bashprefsfile" fi + +echo "Wrote ${cfgfile}" +echo "Wrote ${prefsfile}" +echo "Wrote ${bashprefsfile}" +echo +echo "Next: ensure the Panel remote-server record uses listen_ip=${listen_ip}, listen_port=${listen_port}, and the same key." diff --git a/db/php_aggregator.php b/db/php_aggregator.php index 9d4d0cf..47d8df2 100644 --- a/db/php_aggregator.php +++ b/db/php_aggregator.php @@ -4,8 +4,8 @@ /********** CONFIG (panel DB) **********/ $db = [ 'host' => 'localhost', - 'user' => 'localuser', - 'pass' => 'Pkloyn7yvpht!', + 'user' => 'CHANGE_ME_DB_USER', + 'pass' => 'CHANGE_ME_DB_PASSWORD', 'name' => 'panel' ]; $TABLE_PREFIX = 'gsp_'; diff --git a/docs/AGENT_ACTIVITY_EVENTS.md b/docs/AGENT_ACTIVITY_EVENTS.md index e0b090e..7b23ef7 100644 --- a/docs/AGENT_ACTIVITY_EVENTS.md +++ b/docs/AGENT_ACTIVITY_EVENTS.md @@ -4,7 +4,7 @@ The Linux agent can report meaningful server lifecycle events to the GSP Panel a ## Configuration -Set these values in `Cfg/Config.pm`: +Set these values in `Cfg/Config.pm` under the selected install directory: ```perl agent_event_url => 'https://panel.example.com/agent_event_receiver.php', @@ -13,6 +13,12 @@ remote_server_id => '1', If `agent_event_url` is empty, the agent attempts to derive it from `web_api_url`. The `key` value must match the Panel remote server encryption key because it is used to sign event payloads. +Default new install path: + +```text +/home/gameserver/GSP/Cfg/Config.pm +``` + ## Authentication Events are JSON POST requests signed with HMAC-SHA256: diff --git a/docs/PANEL_INTEGRATION.md b/docs/PANEL_INTEGRATION.md index d6e9421..4bc66bb 100644 --- a/docs/PANEL_INTEGRATION.md +++ b/docs/PANEL_INTEGRATION.md @@ -4,6 +4,17 @@ Workspace reference: [`GSP-WORKSPACE.md`](../../GSP-WORKSPACE.md) The Panel is authoritative. The Linux agent executes the work the Panel requests. +## Current Install And Config Defaults + +New Linux installs default to: + +- agent root: `/home/gameserver/GSP` +- run script: `gsp_agent_run` +- service: `gsp_agent.service` +- log file: `/home/gameserver/GSP/gsp_agent.log` + +Legacy names such as `ogp_agent_run` remain wrappers or protocol compatibility details only. + ## Legacy Workshop RPC The dedicated Panel `steam_workshop` module still uses the legacy `steam_workshop` XML-RPC call. @@ -17,3 +28,14 @@ Current compatibility rule: - each record stores mod string, Workshop item ID, title, installed folder/path, Workshop App ID, install status, and timestamps - the generated job writes `/WorkshopInstallStatus.json` as `running`, `completed`, or `failed` - the generated job prints `GSP_WORKSHOP_INSTALL_COMPLETE` or `GSP_WORKSHOP_INSTALL_FAILED` so the Panel can avoid stale `Update in progress` displays if the generic update screen lingers + +## Server Content Hook Runtime + +The Linux agent prepares this runtime layout for generated content hooks: + +- `_gsp_content/hooks/` +- `_gsp_content/generated/` +- `_gsp_content/runtime/` +- `_gsp_content/runtime/server_content.pids` + +During stop/restart, hook watchdog PIDs are cleaned up before hook application PIDs. Main game server watchdog PIDs should not be written to `server_content.pids`. diff --git a/documentation/agent-guide.md b/documentation/agent-guide.md index 82957d2..2fd1e0b 100644 --- a/documentation/agent-guide.md +++ b/documentation/agent-guide.md @@ -1,95 +1,199 @@ # Linux Agent Operations Guide -Packaged copy of the instructions we keep in the staff wiki so you can view them offline or import them into any other knowledge base. +This guide covers installation, configuration, service management, and troubleshooting for the GSP Linux Agent. ## Purpose -The Linux agent (`ogp_agent.pl`) exposes the RPC endpoint that allows the GameServer Panel to install, start, stop, and monitor game servers on Linux hosts. Every host that runs customer games must run this service. +The Linux agent exposes the RPC endpoint that allows GameServer Panel to install, start, stop, update, and monitor game servers on Linux hosts. -## Supported platforms +## Supported Platforms -- Ubuntu 20.04/22.04/24.04 LTS -- Debian 11/12 -- Rocky/AlmaLinux 8+ -- Any modern distribution with Perl 5.30+, GNU Screen, and rsync +- Ubuntu 20.04, 22.04, and 24.04 LTS +- Debian 11 and 12 +- other modern Linux distributions with Perl, GNU Screen, rsync, sudo, and systemd or compatible service management -## Installation (Ubuntu example) +## Prerequisites + +For Debian and Ubuntu hosts: ```bash -sudo apt update -sudo apt install -y git curl rsync screen perl libxml-parser-perl libpath-class-perl libarchive-zip-perl libhttp-daemon-perl -sudo git clone https://github.com/GameServerPanel/GSP-Agent-Linux.git /opt/gsp-agent -cd /opt/gsp-agent -sudo bash install.sh -sudo bash agent_conf.sh -s "root-password" -u ogp_agent +chmod +x install_agent_prereqs.sh +sudo ./install_agent_prereqs.sh ``` -`agent_conf.sh` writes `/home/ogp_agent/Cfg/Config.pm`. Set: +The prerequisite script installs required Perl modules, `screen`, `rsync`, `sudo`, `unzip`, `git`, `curl`, PureFTPd, and related utilities. It no longer attempts obsolete Ubuntu packages such as `libgcc1:i386`, `lib32gcc1`, `libcrypt-xxtea-perl`, or `libfile-basename-perl`. -| Key | Description | -| --- | ----------- | -| `listen_ip` | Interface to bind (use `0.0.0.0` unless you want to restrict access). | -| `listen_port` | TCP port exposed to the panel. Default is `12679`. | -| `key` | Shared secret copied from the panel → Administration → Game Servers. | -| `web_api_url` | HTTPS URL to `ogp_api.php` on the panel. | -| `agent_event_url` | HTTPS URL to `agent_event_receiver.php` on the panel for lifecycle activity-log events. | -| `remote_server_id` | Panel remote server ID for this agent. Required for signed lifecycle event delivery. | -| `stats_db_*` | Optional MySQL credentials for the resource stats cron. | +The script may show normal `needrestart` output about services, kernels, or VM guests. That output comes from the OS package manager and does not mean the GSP agent installer failed. -## Service management +## Guided Installation ```bash -sudo cp systemd/ogp_agent.service /etc/systemd/system/ -sudo sed -i "s#{OGP_AGENT_PATH}#/opt/gsp-agent#g" /etc/systemd/system/ogp_agent.service -sudo systemctl daemon-reload -sudo systemctl enable --now ogp_agent +chmod +x install.sh agent_conf.sh gsp_agent_run ogp_agent_run +sudo ./install.sh ``` -Logs live next to the binaries (`/opt/gsp-agent/ogp_agent.log`). Individual game servers stream to their own `console.log` files inside each home folder. +Default choices: -## Firewall checklist +- install directory: `/home/gameserver/GSP` +- Linux user: `gameserver` +- service name: `gsp_agent` +- log file: `/home/gameserver/GSP/gsp_agent.log` -1. Allow inbound TCP on the agent port. -2. Allow inbound/outbound UDP/TCP for the games you host. -3. Allow outbound HTTPS to the panel so the agent can talk to `ogp_api.php`. +The installer runs as root from the start. If launched without root and `sudo` is available, it re-executes itself through `sudo` before creating users, copying files, or installing systemd units. -## Upgrades +## Configuration Prompts -1. `cd /opt/gsp-agent && git pull` -2. Stop the service (`sudo systemctl stop ogp_agent`). -3. Re-run `bash install.sh` if new files were added. -4. Start the service (`sudo systemctl start ogp_agent`). -5. Verify the panel shows the agent as “online”. +The installer calls `agent_conf.sh --guided`. The guided configuration asks for the agent key/shared secret, listen IP, listen port, Panel API URL, Panel event receiver URL, remote server ID, FTP management setting, FTP method, game-server autorestart setting, and restart-time agent auto-update setting. + +The agent key/shared secret must match the remote-server record in the GSP Panel. + +## Configuration Files + +Runtime config lives in: + +- `/home/gameserver/GSP/Cfg/Config.pm` +- `/home/gameserver/GSP/Cfg/Preferences.pm` +- `/home/gameserver/GSP/Cfg/bash_prefs.cfg` + +Important `Config.pm` keys: + +- `logfile` +- `listen_ip` +- `listen_port` +- `key` +- `web_api_url` +- `agent_event_url` +- `remote_server_id` + +Important `Preferences.pm` keys: + +- `gsp_manages_ftp` +- `gsp_autorestart_server` +- `ftp_method` +- `PortValidationEnabled` +- `StartupValidationTimeoutSeconds` +- `PortCheckIntervalSeconds` + +Compatibility aliases are still written for older panel/agent paths: + +- `ogp_manages_ftp` +- `ogp_autorestart_server` + +## Folder Layout + +Default runtime folders: + +- `Cfg/` +- `logs/` +- `tmp/` +- `screenlogs/` +- `runtime_status/` +- `events/` +- `_gsp_content/hooks/` +- `_gsp_content/generated/` +- `_gsp_content/runtime/` + +The `_gsp_content/runtime/server_content.pids` file is reserved for generated server-content hook processes. During stop/restart cleanup, hook watchdog PIDs are stopped before hook application PIDs. The main game server watchdog PID should not be stored in this file. + +## systemd Service + +New installs use: + +- `gsp_agent.service` + +Useful commands: + +```bash +sudo systemctl status gsp_agent +sudo systemctl restart gsp_agent +sudo systemctl stop gsp_agent +sudo journalctl -u gsp_agent -f +``` + +The generated unit runs as the selected agent user, sets `WorkingDirectory` to the selected install directory, and starts `gsp_agent_run`. + +## Logs + +- main agent log: `/home/gameserver/GSP/gsp_agent.log` +- systemd journal: `sudo journalctl -u gsp_agent -f` +- screen logs: `/home/gameserver/GSP/screenlogs/` +- activity event offline queue: `/home/gameserver/GSP/events/pending-events.jsonl` + +## Manual Reconfiguration + +```bash +cd /home/gameserver/GSP +./agent_conf.sh --guided --install-dir /home/gameserver/GSP --agent-user gameserver +sudo systemctl restart gsp_agent +``` + +Use noninteractive flags when automating: + +```bash +./agent_conf.sh \ + --install-dir /home/gameserver/GSP \ + --agent-user gameserver \ + --key CHANGE_ME \ + --listen-ip 0.0.0.0 \ + --listen-port 12679 \ + --web-api-url https://panel.example.com/ogp_api.php \ + --agent-event-url https://panel.example.com/agent_event_receiver.php \ + --remote-server-id 1 +``` + +## FTP Configuration + +Default FTP mode is PureFTPd-compatible account management: + +- `gsp_manages_ftp => '1'` +- `ftp_method => 'PureFTPd'` + +Set `gsp_manages_ftp` to `0` if FTP accounts are managed outside the agent. ## Troubleshooting -- `tail -f ogp_agent.log` – handshake failures usually mean the encryption key or port mismatches the panel entry. -- `journalctl -u ogp_agent` – capture Perl stack traces and missing dependency errors. -- `screen -ls` – confirm customer servers are running in screen sessions. -- `nc -vz panel.example.com 12679` from the panel host – ensures the agent port is reachable. +Authentication failed or Panel shows agent offline: -## Server lifecycle tracking +- confirm `key` in `Cfg/Config.pm` matches the Panel remote-server key +- confirm `listen_port` is reachable from the Panel host +- check firewall rules -The Linux agent now keeps per-home PID metadata under: +Polkit or systemctl authentication failure: -- `runtime_status/pid-.kv` +- run `sudo ./install.sh` +- do not start with a non-root partial install +- inspect `sudo journalctl -u gsp_agent -f` -This metadata is used by stop/restart verification in addition to screen and port checks. +Wrong install path or old `/home/gameserver/OGP` log path: -Stop escalation now verifies all of the following are cleared before success: +- rerun `./agent_conf.sh --guided --install-dir /home/gameserver/GSP` +- confirm `Cfg/Config.pm` has `logfile => '/home/gameserver/GSP/gsp_agent.log'` +- confirm the systemd unit `WorkingDirectory` points to `/home/gameserver/GSP` -1. managed screen session -2. tracked process PID(s) -3. listening game port +Missing packages: -Lifecycle control no longer uses game-home `SERVER_STOPPED` marker files. +- rerun `sudo ./install_agent_prereqs.sh` +- confirm `screen`, `rsync`, `perl`, `git`, `curl`, and required Perl modules are installed -See `docs/AGENT_ACTIVITY_EVENTS.md` for Panel activity-log event delivery, offline queue behavior, and troubleshooting. -Explicit stop intent and autorestart suppression are now controlled through agent-owned runtime status hints (`STOPPING`/`STOPPED`) and verified runtime checks. +Service failed: -Restart remains stop-first and waits 60 seconds, then re-verifies stop completion before starting again to avoid duplicate instances. +- run `sudo systemctl status gsp_agent` +- run `sudo journalctl -u gsp_agent -f` +- run `perl -c /home/gameserver/GSP/ogp_agent.pl` -## Related docs +Config missing or invalid: -- [`GSP/documentation/admin-guide.md`](https://github.com/GameServerPanel/GSP/tree/main/documentation) – Panel-side instructions plus XML authoring notes. -- [`GSP-Agent-Windows/documentation/agent-guide.md`](https://github.com/GameServerPanel/GSP-Agent-Windows/tree/main/documentation/agent-guide.md) – Windows counterpart. +- run `./agent_conf.sh --guided` +- replace placeholder `CHANGE_ME_PANEL_AGENT_KEY` +- verify `Cfg/Config.pm` is readable by the agent user + +## Clean Reinstall + +```bash +sudo systemctl stop gsp_agent || true +sudo systemctl disable gsp_agent || true +sudo rm -f /etc/systemd/system/gsp_agent.service +sudo systemctl daemon-reload +``` + +Only remove `/home/gameserver/GSP` after confirming it does not contain customer server data, logs, SteamCMD data, or local configuration that must be preserved. diff --git a/gsp_agent_run b/gsp_agent_run new file mode 100755 index 0000000..936739c --- /dev/null +++ b/gsp_agent_run @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +set -euo pipefail + +AGENTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +AGENT="${AGENTDIR}/ogp_agent.pl" +PID_FILE="" +TIMEOUT=10 + +while [ "$#" -gt 0 ]; do + case "$1" in + -pidfile) + PID_FILE="${2:-}" + shift 2 + ;; + *) + shift + ;; + esac +done + +mkdir -p "${AGENTDIR}/logs" "${AGENTDIR}/screenlogs" "${AGENTDIR}/tmp" "${AGENTDIR}/_gsp_content/hooks" "${AGENTDIR}/_gsp_content/generated" "${AGENTDIR}/_gsp_content/runtime" +touch "${AGENTDIR}/_gsp_content/runtime/server_content.pids" 2>/dev/null || true + +if [ "$(id -u)" -eq 0 ]; then + echo "ERROR: Do not run the GSP agent as root. Run it as the configured agent user." >&2 + exit 1 +fi + +if [ ! -f "${AGENTDIR}/Cfg/Config.pm" ]; then + echo "ERROR: Missing ${AGENTDIR}/Cfg/Config.pm. Run ./agent_conf.sh --guided." >&2 + exit 1 +fi + +if grep -Eq "CHANGE_ME_PANEL_AGENT_KEY|key[[:space:]]*=>[[:space:]]*''" "${AGENTDIR}/Cfg/Config.pm"; then + echo "ERROR: ${AGENTDIR}/Cfg/Config.pm still has a placeholder agent key." >&2 + exit 1 +fi + +if [ ! -x "$AGENT" ]; then + chmod ug+x "$AGENT" 2>/dev/null || true +fi + +if [ -n "$PID_FILE" ]; then + echo "$$" > "$PID_FILE" +fi + +cd "$AGENTDIR" + +while true; do + perl "$AGENT" "$@" || rc=$? + rc="${rc:-0}" + if [ "$rc" -eq 0 ]; then + echo "$(date): GSP Agent stopped cleanly" + exit 0 + fi + echo "$(date): GSP Agent exited with status ${rc}; restarting in ${TIMEOUT}s" >&2 + sleep "$TIMEOUT" + unset rc +done diff --git a/includes/ogp_agent.init b/includes/ogp_agent.init index 6da2814..4994d84 100644 --- a/includes/ogp_agent.init +++ b/includes/ogp_agent.init @@ -7,16 +7,16 @@ agent_user=OGP_USER # Start function. start() { - echo "Starting OGP Agent..." + echo "Starting GSP Agent..." cd $agent_dir - su -c "screen -d -m -t ogp_agent -c ogp_screenrc -S ogp_agent ./ogp_agent_run -pidfile ogp_agent_run.pid" $agent_user &> $agent_dir/ogp_agent.svc & + su -c "screen -d -m -t gsp_agent -c ogp_screenrc -S gsp_agent ./gsp_agent_run -pidfile gsp_agent_run.pid" $agent_user &> $agent_dir/gsp_agent.svc & echo } # Stop function. stop() { - echo "Stopping OGP Agent..." - kill `cat $agent_dir/ogp_agent_run.pid` + echo "Stopping GSP Agent..." + kill `cat $agent_dir/gsp_agent_run.pid` } restart() { @@ -35,7 +35,7 @@ case $1 in restart ;; *) - echo "Usage: ogp_agent {start|stop|restart}" + echo "Usage: gsp_agent {start|stop|restart}" exit 1 ;; esac diff --git a/includes/ogp_agent.init.dbn b/includes/ogp_agent.init.dbn index 4aa247e..d9310a8 100644 --- a/includes/ogp_agent.init.dbn +++ b/includes/ogp_agent.init.dbn @@ -8,8 +8,8 @@ # Should-Stop: $all # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 -# Short-Description: Start and stop the OGP Agent -# Description: Start and stop the OGP Agent +# Short-Description: Start and stop the GSP Agent +# Description: Start and stop the GSP Agent ### END INIT INFO # @@ -26,9 +26,9 @@ then fi start() { - if [ -e "$agent_dir/ogp_agent_run.pid" ] + if [ -e "$agent_dir/gsp_agent_run.pid" ] then - pid=$(cat $agent_dir/ogp_agent_run.pid) + pid=$(cat $agent_dir/gsp_agent_run.pid) out=$(kill -0 $pid > /dev/null 2>&1) if [ $? == 0 ] then @@ -90,14 +90,14 @@ start() { fi cd $agent_dir - out=$(su -c "screen -d -m -t ogp_agent -c ogp_screenrc -S ogp_agent ./ogp_agent_run -pidfile ogp_agent_run.pid" $agent_user >/dev/null 2>&1) + out=$(su -c "screen -d -m -t gsp_agent -c ogp_screenrc -S gsp_agent ./gsp_agent_run -pidfile gsp_agent_run.pid" $agent_user >/dev/null 2>&1) return 0 } stop() { - if [ -e "$agent_dir/ogp_agent_run.pid" ] + if [ -e "$agent_dir/gsp_agent_run.pid" ] then - pid=$(cat $agent_dir/ogp_agent_run.pid) + pid=$(cat $agent_dir/gsp_agent_run.pid) kill -0 $pid > /dev/null 2>&1 if [ $? == 0 ] then @@ -126,7 +126,7 @@ case "${1:-''}" in RETVAL=$? ;; *) - echo "Usage: service ogp_agent start|stop|restart" + echo "Usage: service gsp_agent start|stop|restart" exit 1 ;; esac diff --git a/includes/ogp_agent.init.gentoo b/includes/ogp_agent.init.gentoo index eaaf0ac..64657fa 100644 --- a/includes/ogp_agent.init.gentoo +++ b/includes/ogp_agent.init.gentoo @@ -12,14 +12,13 @@ depend() { } start() { - ebegin "Starting OGP Agent" - start-stop-daemon --verbose --chdir $agent_dir --start --background --user $agent_user -e PWD="$agent_dir" --exec screen -d -m -t ogp_agent -c ogp_screenrc -S ogp_agent ./ogp_agent_run -pidfile ogp_agent_run.pid - eend $? "Failed to start OGP Agent" + ebegin "Starting GSP Agent" + start-stop-daemon --verbose --chdir $agent_dir --start --background --user $agent_user -e PWD="$agent_dir" --exec screen -d -m -t gsp_agent -c ogp_screenrc -S gsp_agent ./gsp_agent_run -pidfile gsp_agent_run.pid + eend $? "Failed to start GSP Agent" } stop() { - ebegin "Stopping OGP Agent" - start-stop-daemon --stop --quiet --pidfile $agent_dir/ogp_agent_run.pid - eend $? "Failed to stop OGP Agent" + ebegin "Stopping GSP Agent" + start-stop-daemon --stop --quiet --pidfile $agent_dir/gsp_agent_run.pid + eend $? "Failed to stop GSP Agent" } - diff --git a/includes/ogp_agent.init.rh b/includes/ogp_agent.init.rh index ec6b4e8..ec3e94f 100644 --- a/includes/ogp_agent.init.rh +++ b/includes/ogp_agent.init.rh @@ -1,16 +1,16 @@ #!/bin/sh # -# Startup/shutdown script for the OGP Agent. +# Startup/shutdown script for the GSP Agent. # # Linux chkconfig stuff: # # chkconfig: 2345 88 10 -# description: Startup/shutdown script for the OGP Agent +# description: Startup/shutdown script for the GSP Agent agent_dir=OGP_AGENT_DIR agent_user=OGP_USER -service=ogp_agent +service=gsp_agent # Source function library. if [ -f /etc/rc.d/init.d/functions ] ; then @@ -23,7 +23,7 @@ if [ "$( whoami )" != "root" ] then if [ -f "/usr/bin/sudo" ] && [ "$( groups $agent_user | grep "\bsudo\b" )" != "" ] then - sudo /etc/init.d/ogp_agent ${1:-''} + sudo /etc/init.d/gsp_agent ${1:-''} exit else echo "Permission denied." @@ -32,9 +32,9 @@ then fi start() { - echo -n "Starting OGP Agent: " - if [ -e "$agent_dir/ogp_agent_run.pid" ]; then - PID=`cat $agent_dir/ogp_agent_run.pid` + echo -n "Starting GSP Agent: " + if [ -e "$agent_dir/gsp_agent_run.pid" ]; then + PID=`cat $agent_dir/gsp_agent_run.pid` RET=$(kill -s 0 $PID &> /dev/null; echo $?) if [ $RET -eq 0 ]; then echo -n "already running." @@ -100,22 +100,22 @@ start() { fi cd $agent_dir - su -c "screen -d -m -t ogp_agent -c ogp_screenrc -S ogp_agent ./ogp_agent_run -pidfile ogp_agent_run.pid" $agent_user &> $agent_dir/ogp_agent.svc & + su -c "screen -d -m -t gsp_agent -c ogp_screenrc -S gsp_agent ./gsp_agent_run -pidfile gsp_agent_run.pid" $agent_user &> $agent_dir/gsp_agent.svc & echo -n "started successfully." bold=`tput bold` normal=`tput sgr0` echo - echo "Use ${bold}sudo su -c 'screen -S ogp_agent -r' $agent_user${normal} to attach the agent screen," + echo "Use ${bold}sudo su -c 'screen -S gsp_agent -r' $agent_user${normal} to attach the agent screen," echo "and ${bold}ctrl+A+D${normal} to detach it." return 0 } stop() { # Stop daemon - echo -n "Stopping OGP Agent: " - if [ -f $agent_dir/ogp_agent_run.pid ] + echo -n "Stopping GSP Agent: " + if [ -f $agent_dir/gsp_agent_run.pid ] then - PID=`cat $agent_dir/ogp_agent_run.pid` + PID=`cat $agent_dir/gsp_agent_run.pid` RET=$(kill $PID &> /dev/null; echo $?) if [ $RET -ne 0 ]; then echo -n "not running." @@ -123,7 +123,7 @@ stop() { echo -n "stopped successfully." fi else - echo -n "PID file not found ($agent_dir/ogp_agent_run.pid)" + echo -n "PID file not found ($agent_dir/gsp_agent_run.pid)" fi echo return 0 @@ -145,7 +145,7 @@ case "$1" in RETVAL=$? ;; *) - echo "Usage: service ogp_agent start|stop|restart" + echo "Usage: service gsp_agent start|stop|restart" RETVAL=1 echo ;; diff --git a/install.sh b/install.sh old mode 100644 new mode 100755 index 7165d81..5d58f3f --- a/install.sh +++ b/install.sh @@ -1,351 +1,207 @@ -#!/bin/bash +#!/usr/bin/env bash -# -# OGP - Open Game Panel -# Copyright (C) Copyright (C) 2008 - 2013 The OGP Development Team -# -# http://www.opengamepanel.org/ -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# +set -euo pipefail -# Parameters can be passed into the install.sh script to automate OGP updates -# $1 = Operation Type (Used as opType) -# $2 = OGP User (Used as ogpAgentUser) -# $3 = OGP User Sudo Pass (Used as ogpUserPass) -# $4 = Install Path (Used as ogpInsPath) +DEFAULT_INSTALL_DIR="/home/gameserver/GSP" +DEFAULT_AGENT_USER="gameserver" +DEFAULT_SERVICE_NAME="gsp_agent" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ORIG_ARGS=("$@") +usage() { + cat <&2 exit 1 } -if [ "X`which screen &> /dev/null;echo $?`" != "X0" ]; then - failed "You need to install software called 'screen', before you can install OGP agent."; -fi - -if [ "X`which sed &> /dev/null;echo $?`" != "X0" ]; then - failed "You need to install software called 'sed', before you can install OGP agent."; -fi -echo -clear -echo "#######################################################################" -echo "# OGP Agent installation and configuration" -echo "# This program will:" -echo "# Create ${DEFAULT_AGENT_HOME} or user defined directory" -echo "# Copy ogp_agent files to ${DEFAULT_AGENT_HOME} or user defined dir" -echo "# Copy the ogp_agent init script to /etc/init.d or user defined dir" -echo "# Create an initial configuration file" -echo "# Thank you for using OGP. http://www.opengamepanel.org/" -echo "#######################################################################" -echo - - -if [ "X`which rsync &> /dev/null;echo $?`" != "X0" ]; then - echo "*** WARNING **** missing rsync client. It is not required, but needed to use the rsync game installer"; -fi - -if [ "X`whoami`" != "Xroot" ] -then - echo - echo "Detected non-root install..." - username=`whoami` - echo -n "Enter sudo password: "; - read sudo_password; -else - echo "Next you need to type the username of the user that owns the agent homes."; - echo "This user must own (have access to) all the game home directories that you" - echo "want to run with this agent and must to be in sudoers list so it can perform" - echo "administrative tasks.";echo - while [ 1 ] - do - if [ ! -z "$ogpAgentUser" ] ; then - username="$ogpAgentUser" - else - echo -n "Enter user name: "; - read username; - fi - - if [ -z "$ogpUserPass" ] ; then - echo -n "Enter user password: "; - read sudo_password; - else - sudo_password="$ogpUserPass" - fi - - if [ -z "${username}" ] - then - echo "Username can not be empty.";echo - continue; - fi - - if [ "Xroot" == "X${username}" ] - then - echo "'${username}' can not be used as user for agent.";echo - continue; - fi - - ID_OF_USER=`id -u ${username} 2> /dev/null` - if [ $? != 0 ] - then - echo "User with entered username (${username}) does not exist.";echo - continue; - fi - - break; - done -fi - -detectSystemD - -readonly AGENT_USER_HOME="`cat /etc/passwd | grep "^${username}:" | cut -d':' -f6`/OGP/" - -echo -echo "Next the directory for the agent needs to be chosen. The default directory"; -echo "Should be fine in most of the cases." -echo - -if [ -z "$ogpInsPath" ]; then - echo "Where do you want to install the agent?" - echo -n "[Default is ${AGENT_USER_HOME}]: " - read agent_home -else - agent_home="$ogpInsPath" -fi - -if [ -z "${agent_home}" ] -then - agent_home=$AGENT_USER_HOME -fi - -# Try to prevent users from doing damage to their systems. -case ${agent_home} in - /bin*|/boot*|/dev*|/etc*|/lib*|/proc*|/root*|/sbin*|/sys*|/) - failed "The agent home can not be ${agent_home}"; - ;; -esac - -echo "Agent install dir is ${agent_home}" -echo -agent_home=${agent_home%/} - -if [ ! -e ${agent_home} ] -then - mkdir -p ${agent_home} || failed "Failed to create the directory (${agent_home}) for agent." -elif [ ! -w ${agent_home} ] -then - failed "You do not have write permissions to the directory you assigned as agent home (${agent_home})." -fi - -if [ "X`whoami`" == "Xroot" ]; -then - readonly DEFAULT_INIT_DIR="/etc/init.d/" -else - readonly DEFAULT_INIT_DIR="${agent_home}/" -fi - -if [ -z "$systemdPresent" ]; then - - if [ "X`uname`" != "XLinux" ] - then - echo - echo "Detected non-Linux platform..." - echo "Where do you want to put the init scripts?" - echo -n "[Default ${DEFAULT_INIT_DIR}]: " - read init_dir +require_root() { + if [ "$(id -u)" -eq 0 ]; then + return 0 fi - - if [ -z "$opType" ]; then - echo "Where do you want to put the init scripts?" - echo -n "[Default ${DEFAULT_INIT_DIR}]:" - read init_dir + if command -v sudo >/dev/null 2>&1; then + echo "System service installation requires root. Re-running through sudo..." + exec sudo -E bash "$0" "$@" fi + fail "Run this installer as root: sudo bash install.sh" +} -else - init_dir=${agent_home} -fi +validate_safe_path() { + local path="$1" + case "$path" in + ""|"/"|/bin|/bin/*|/boot|/boot/*|/dev|/dev/*|/etc|/etc/*|/lib|/lib/*|/lib64|/lib64/*|/proc|/proc/*|/root|/root/*|/run|/run/*|/sbin|/sbin/*|/sys|/sys/*|/usr|/usr/*|/var|/var/*) + fail "Refusing unsafe install directory: $path" + ;; + esac +} -if [ -z "${init_dir}" ] -then - init_dir=${DEFAULT_INIT_DIR} -fi -init_dir=${init_dir%/} -echo "Copying all files and folders to installation directory..." -# Use rsync if available, otherwise fallback to find/cp -if command -v rsync >/dev/null 2>&1; then - rsync -a --exclude install.sh ./ "${agent_home}/" || failed "Failed to copy files with rsync to ${agent_home}." -else - find . -mindepth 1 -not -name install.sh -exec cp -rf --parents {} "${agent_home}/" \; -fi +install_service() { + local install_dir="$1" + local agent_user="$2" + local service_name="$3" + local unit_path="/etc/systemd/system/${service_name}.service" -# Create the directory for configs. -mkdir -p ${agent_home}/Cfg || failed "Failed to create ${agent_home}/Cfg dir." + command -v systemctl >/dev/null 2>&1 || { + echo "systemd was not detected. Skipping service installation." + return 0 + } + + cp "${install_dir}/systemd/gsp_agent.service" "$unit_path" + sed -i \ + -e "s#{GSP_AGENT_PATH}#${install_dir}#g" \ + -e "s#{GSP_AGENT_USER}#${agent_user}#g" \ + -e "s#{GSP_AGENT_RUN_SCRIPT}#${install_dir}/gsp_agent_run#g" \ + -e "s#{GSP_AGENT_PID_FILE}#${install_dir}/gsp_agent_run.pid#g" \ + "$unit_path" + + systemctl daemon-reload + systemctl enable "$service_name.service" + if ask_yes_no "Start ${service_name}.service now?" "yes"; then + systemctl restart "$service_name.service" + fi +} + +INSTALL_DIR="" +AGENT_USER="" +SERVICE_NAME="" +INSTALL_SERVICE=1 +OFFER_PREREQS=1 + +while [ "$#" -gt 0 ]; do + case "$1" in + --install-dir) INSTALL_DIR="${2:-}"; shift 2 ;; + --agent-user) AGENT_USER="${2:-}"; shift 2 ;; + --service-name) SERVICE_NAME="${2:-}"; shift 2 ;; + --no-service) INSTALL_SERVICE=0; shift ;; + --no-prereqs) OFFER_PREREQS=0; shift ;; + --help|-h) usage; exit 0 ;; + *) fail "Unknown option: $1" ;; + esac +done + +require_root "${ORIG_ARGS[@]}" + +echo "GSP Linux Agent installer" +echo +echo "This installer configures the Linux execution agent for GameServer Panel." +echo "Use the same key/password that is configured for this remote server in the Panel." echo -if [ -e /etc/gentoo-release ] -then - echo "Copying ogp_agent.init.gentoo to $init_dir - Gentoo Specific Init" - init_file_template='includes/ogp_agent.init.gentoo' -elif [ -e /etc/sysconfig ] && [ ! -e /etc/debian_version ] -then - echo "Copying ogp_agent.init.rh to $init_dir - Redhat Style Init (also SuSE, and Mandrake)" - init_file_template='includes/ogp_agent.init.rh' -elif [ -e /etc/debian_version ] -then - echo "Copying ogp_agent.init.dbn to $init_dir - Debian Style Init" - init_file_template='includes/ogp_agent.init.dbn' -else - echo "Copying the generic init script because I don't know what kind of Linux distro this is" - init_file_template='includes/ogp_agent.init' +if [ "$OFFER_PREREQS" -eq 1 ] && [ -x "${SCRIPT_DIR}/install_agent_prereqs.sh" ]; then + if ask_yes_no "Run install_agent_prereqs.sh before installing?" "yes"; then + bash "${SCRIPT_DIR}/install_agent_prereqs.sh" + fi +elif [ "$OFFER_PREREQS" -eq 1 ]; then + echo "Prerequisite installer is not executable. Run: chmod +x install_agent_prereqs.sh && sudo ./install_agent_prereqs.sh" fi -init_file=${init_dir}/ogp_agent +INSTALL_DIR="${INSTALL_DIR:-$(ask "Install directory" "$DEFAULT_INSTALL_DIR")}" +AGENT_USER="${AGENT_USER:-$(ask "Agent Linux user" "$DEFAULT_AGENT_USER")}" +SERVICE_NAME="${SERVICE_NAME:-$(ask "systemd service name" "$DEFAULT_SERVICE_NAME")}" -cp -f $init_file_template $init_file || failed "Failed to create init file ($init_file)." -# Next we replace the OGP_AGENT_DIR with the actual dir in init file. -sed -i "s|OGP_AGENT_DIR|${agent_home}|" ${init_file} || failed "Failed to modify init file ($init_file)." -sed -i "s|OGP_USER|${username}|" ${init_file} || failed "Failed to modify init file ($init_file)." -chmod a+x $init_file +validate_safe_path "$INSTALL_DIR" +INSTALL_DIR="${INSTALL_DIR%/}" -if [ "$init_dir" == "$agent_home" ] && [ ! -z "$systemdPresent" ]; then - init_file=${init_dir}/ogp_agent_init - mv ${init_dir}/ogp_agent ${init_dir}/ogp_agent_init - copySystemDInit +if ! id "$AGENT_USER" >/dev/null 2>&1; then + if ask_yes_no "User ${AGENT_USER} does not exist. Create it?" "yes"; then + useradd --create-home --shell /bin/bash "$AGENT_USER" + else + fail "Agent user is required." + fi fi -echo; - -echo "Changing files owner to user ${username}..."; -# Group of the files in agent_home can differ from the user so -# lets leave them as they are. So no chown user:group here. -chown --preserve-root -R ${username} ${agent_home} || failed "Failed to chmod the agent_home ${agent_home} for user ${username}." - -echo "Setting Permissions on files in ${agent_home}..." -if [ -e "${init_dir}/ogp_agent" ]; then - chmod 750 ${init_dir}/ogp_agent || failed "Failed to chmod ${init_dir}/ogp_agent to 750." -fi -if [ -e "${init_dir}/ogp_agent_init" ]; then - chmod 750 ${init_dir}/ogp_agent_init || failed "Failed to chmod ${init_dir}/ogp_agent_init to 750." -fi -chmod 750 ${agent_home}/ogp_agent.pl || failed "Failed to chmod ${agent_home}/ogp_agent.pl to 750." -chmod 750 ${agent_home}/ogp_agent_run || failed "Failed to chmod ${agent_home}/ogp_agent_run to 750." - -echo "Install Successful!" -echo "Now configuring..." -echo "" - -# Run the configuration script -chmod +x ${agent_home}/agent_conf.sh - -if [ -z "$opType" ]; then - bash ${agent_home}/agent_conf.sh -s $sudo_password -u $username +mkdir -p "$INSTALL_DIR" +if [ "$SCRIPT_DIR" != "$INSTALL_DIR" ]; then + rsync -a --exclude='.git/' --exclude='Cfg/Config.pm' --exclude='Cfg/Preferences.pm' --exclude='Cfg/bash_prefs.cfg' "${SCRIPT_DIR}/" "${INSTALL_DIR}/" fi -echo "Attempting to start the Open Game Panel (OGP) agent..." +mkdir -p \ + "${INSTALL_DIR}/Cfg" \ + "${INSTALL_DIR}/logs" \ + "${INSTALL_DIR}/tmp" \ + "${INSTALL_DIR}/screenlogs" \ + "${INSTALL_DIR}/runtime_status" \ + "${INSTALL_DIR}/events" \ + "${INSTALL_DIR}/_gsp_content/hooks" \ + "${INSTALL_DIR}/_gsp_content/generated" \ + "${INSTALL_DIR}/_gsp_content/runtime" -systemctl daemon-reload -chkconfig ogp_agent on -rc-update add ogp_agent default -update-rc.d ogp_agent defaults -systemctl enable ogp_agent.service -service ogp_agent restart +touch "${INSTALL_DIR}/_gsp_content/runtime/server_content.pids" -echo; -echo "OGP installation complete!" -echo +chmod +x "${INSTALL_DIR}/gsp_agent_run" "${INSTALL_DIR}/ogp_agent_run" "${INSTALL_DIR}/agent_conf.sh" "${INSTALL_DIR}/install_agent_prereqs.sh" 2>/dev/null || true +chown -R "${AGENT_USER}:${AGENT_USER}" "$INSTALL_DIR" -exit 0 \ No newline at end of file +echo +echo "Configuring agent files..." +bash "${INSTALL_DIR}/agent_conf.sh" \ + --install-dir "$INSTALL_DIR" \ + --agent-user "$AGENT_USER" \ + --guided + +if [ "$INSTALL_SERVICE" -eq 1 ]; then + install_service "$INSTALL_DIR" "$AGENT_USER" "$SERVICE_NAME" +fi + +cat </dev/null 2>&1 || ! command -v dpkg >/dev/null 2>&1; then + echo "This prerequisite installer currently supports apt/dpkg systems only." + echo "Install equivalent Perl, screen, rsync, sudo, unzip, git, curl, and FTP packages manually." + exit 1 +fi + +echo "Installing GSP Linux Agent prerequisites for Debian/Ubuntu." +echo "Package manager output from needrestart about services, VM guests, or kernel state is normal OS maintenance output." + apt-get update -# Install core required packages from original prerequisites -apt-get install -y \ - libxml-parser-perl \ - libpath-class-perl \ - perl-modules \ - screen \ - rsync \ - sudo \ - e2fsprogs \ - unzip \ - subversion \ - libarchive-extract-perl \ - pure-ftpd \ - libarchive-zip-perl \ - libc6 \ - libgcc1 \ - git \ - curl \ - libhttp-daemon-perl +required_packages=( + perl + libxml-parser-perl + libpath-class-perl + libarchive-extract-perl + libarchive-zip-perl + libhttp-daemon-perl + libfrontier-rpc-perl + libfile-copy-recursive-perl + libschedule-cron-perl + libio-compress-perl + libcompress-raw-zlib-perl + libfile-find-rule-perl + libwww-perl + screen + rsync + sudo + e2fsprogs + unzip + subversion + git + curl + ca-certificates + pure-ftpd + libc6 +) -# Install 32-bit compatibility libraries (may fail on some systems, continue anyway) -apt-get install -y libc6-i386 || echo "Warning: Could not install libc6-i386" -apt-get install -y libgcc1:i386 || echo "Warning: Could not install libgcc1:i386" -apt-get install -y lib32gcc1 || echo "Warning: Could not install lib32gcc1" +optional_packages=( + libdbi-perl + libdbd-mysql-perl + libfcgi-perl + apache2 + php + php-mysql +) -# Install additional modern packages for current OGP agent -apt-get install -y \ - libdbi-perl \ - libdbd-mysql-perl \ - libfrontier-rpc-perl \ - libfile-copy-recursive-perl \ - libcrypt-xxtea-perl \ - libschedule-cron-perl \ - libmime-base64-perl \ - libgetopt-long-descriptive-perl \ - libio-compress-perl \ - libcompress-raw-zlib-perl \ - libfile-find-rule-perl \ - libfile-basename-perl \ - libfcgi-perl \ - libwww-perl +apt-get install -y "${required_packages[@]}" -# Optional: For FTP management (pure-ftpd already installed above) -# apt-get install -y proftpd-basic +for pkg in "${optional_packages[@]}"; do + if ! apt-get install -y "$pkg"; then + echo "Warning: optional package ${pkg} could not be installed." + fi +done -# Optional: For web panel integration -apt-get install -y apache2 php php-mysql || echo "Warning: Optional web packages could not be installed" +echo +echo "32-bit compatibility packages are useful for some legacy SteamCMD/game-server binaries." +read -r -p "Enable i386 architecture and install 32-bit compatibility packages? [yes]: " install_i386 +install_i386="${install_i386:-yes}" +if [[ "$install_i386" =~ ^[Yy]([Ee][Ss])?$ ]]; then + if ! dpkg --print-foreign-architectures | grep -qx i386; then + dpkg --add-architecture i386 + apt-get update + fi + for pkg in libc6-i386 lib32gcc-s1 libgcc-s1:i386; do + if ! apt-get install -y "$pkg"; then + echo "Warning: optional 32-bit package ${pkg} could not be installed." + fi + done +fi -# Done +cat <<'EOF' -echo "All required packages for OGP Agent have been installed." -echo "Note: Some 32-bit compatibility libraries may not be available on all systems." -echo "This is normal for modern 64-bit only distributions." +Prerequisite installation finished. + +Notes: +- MIME::Base64, File::Basename, and Getopt::Long are Perl core modules and are not installed as separate apt packages. +- Crypt::XXTEA is bundled in this repository under Crypt/XXTEA.pm, so libcrypt-xxtea-perl is not required from apt. +- needrestart messages about services or VM guests are normal package-manager output after dependency installation. +EOF diff --git a/ogp_agent.pl b/ogp_agent.pl index e581919..febd44a 100644 --- a/ogp_agent.pl +++ b/ogp_agent.pl @@ -1,10 +1,9 @@ #!/usr/bin/perl # -# OGP - Open Game Panel +# GameServer Panel Linux Agent +# Forked from the Open Game Panel agent. # Copyright (C) 2008 - 2018 The OGP Development Team # -# http://www.opengamepanel.org/ -# # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 @@ -125,6 +124,21 @@ GetOptions( 'log-stdout' => \$log_std_out ); +sub preference_value +{ + my ($primary, $legacy, $default) = @_; + return $Cfg::Preferences{$primary} if(defined($Cfg::Preferences{$primary})); + return $Cfg::Preferences{$legacy} if(defined($legacy) && defined($Cfg::Preferences{$legacy})); + return $default; +} + +sub preference_enabled +{ + my ($primary, $legacy, $default) = @_; + my $value = preference_value($primary, $legacy, $default); + return defined($value) && $value eq "1" ? 1 : 0; +} + # Starting the agent as root user is not supported anymore. if ($< == 0) { @@ -188,6 +202,76 @@ sub agent_event_remote_server_id return ""; } +sub prepare_server_content_runtime_without_decrypt +{ + my ($home_path) = @_; + return if(!defined($home_path) || $home_path eq ""); + foreach my $dir ( + "$home_path/_gsp_content", + "$home_path/_gsp_content/hooks", + "$home_path/_gsp_content/generated", + "$home_path/_gsp_content/runtime" + ) + { + mkpath($dir) if(!-d $dir); + } + my $pid_file = "$home_path/_gsp_content/runtime/server_content.pids"; + if(!-e $pid_file && open(my $fh, '>', $pid_file)) + { + close($fh); + } +} + +sub cleanup_server_content_pid_group_without_decrypt +{ + my ($pid_file, $wanted_role, $as_user) = @_; + return if(!-f $pid_file); + my @remaining; + if(!open(my $in, '<', $pid_file)) + { + logger "Could not read server content PID file $pid_file: $!"; + return; + } + while(my $line = <$in>) + { + chomp($line); + my $original = $line; + $line =~ s/^\s+|\s+$//g; + next if($line eq ""); + my ($pid) = $line =~ /(?:^|[^\d])(\d{2,})(?:[^\d]|$)/; + my $role = ($line =~ /watchdog/i) ? "watchdog" : "app"; + $role = "main_server" if($line =~ /main[_-]?server/i); + if(defined($pid) && $role eq $wanted_role) + { + logger "Stopping server-content $role PID $pid from $pid_file."; + sudo_exec_without_decrypt("kill -TERM $pid >/dev/null 2>&1 || true", $as_user); + sleep 1 if($wanted_role eq "watchdog"); + sudo_exec_without_decrypt("kill -KILL $pid >/dev/null 2>&1 || true", $as_user); + next; + } + push(@remaining, $original) if($role ne "watchdog" && $role ne "app"); + } + close($in); + if(open(my $out, '>', $pid_file)) + { + foreach my $line (@remaining) + { + print $out "$line\n"; + } + close($out); + } +} + +sub cleanup_server_content_hooks_without_decrypt +{ + my ($home_path, $as_user) = @_; + return if(!defined($home_path) || $home_path eq ""); + prepare_server_content_runtime_without_decrypt($home_path); + my $pid_file = "$home_path/_gsp_content/runtime/server_content.pids"; + cleanup_server_content_pid_group_without_decrypt($pid_file, "watchdog", $as_user); + cleanup_server_content_pid_group_without_decrypt($pid_file, "app", $as_user); +} + sub new_correlation_id { return time() . "-" . $$ . "-" . int(rand(1000000000)); @@ -565,7 +649,7 @@ open(PID, '>', AGENT_PID_FILE) print PID "$$\n"; close(PID); -logger "Open Game Panel - Agent started - " +logger "GameServer Panel - Agent started - " . AGENT_VERSION . " - port " . AGENT_PORT @@ -688,7 +772,7 @@ my $d = Frontier::Daemon::OGP::Forking->new( LocalPort => AGENT_PORT, LocalAddr => AGENT_IP, ReuseAddr => '1' -) or die "Couldn't start OGP Agent: $!"; +) or die "Couldn't start GSP Agent: $!"; sub backup_home_log { @@ -1608,6 +1692,7 @@ sub universal_start_without_decrypt my $cli_bin; my $command; my $run_before_start; + prepare_server_content_runtime_without_decrypt($home_path); # Replace any OGP variables found in the command line $startup_cmd = replace_OGP_Env_Vars($screen_id, $home_id, $home_path, $startup_cmd, $game_key); @@ -1621,7 +1706,7 @@ sub universal_start_without_decrypt $command = "taskset -c $cpu wine $server_exe $startup_cmd"; } - if(defined($Cfg::Preferences{ogp_autorestart_server}) && $Cfg::Preferences{ogp_autorestart_server} eq "1"){ + if(preference_enabled('gsp_autorestart_server', 'ogp_autorestart_server', '1')){ $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars, undef, $status_hint_file); }else{ $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars, 1); @@ -1636,7 +1721,7 @@ sub universal_start_without_decrypt $command = "taskset -c $cpu $startup_cmd"; } - if(defined($Cfg::Preferences{ogp_autorestart_server}) && $Cfg::Preferences{ogp_autorestart_server} eq "1"){ + if(preference_enabled('gsp_autorestart_server', 'ogp_autorestart_server', '1')){ $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars, undef, $status_hint_file); }else{ $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars, 1); @@ -1651,7 +1736,7 @@ sub universal_start_without_decrypt $command = "taskset -c $cpu ./$server_exe $startup_cmd"; } - if(defined($Cfg::Preferences{ogp_autorestart_server}) && $Cfg::Preferences{ogp_autorestart_server} eq "1"){ + if(preference_enabled('gsp_autorestart_server', 'ogp_autorestart_server', '1')){ $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars, undef, $status_hint_file); }else{ $cli_bin = create_screen_cmd_loop($screen_id, $command, $envVars, 1); @@ -2093,6 +2178,7 @@ sub stop_server_without_decrypt my $screen_id = create_screen_id(SCREEN_TYPE_HOME, $home_id); my $as_user = find_user_by_screen_id($screen_id); + cleanup_server_content_hooks_without_decrypt($home_path, $as_user); if ($control_password !~ /^\s*$/ and $control_protocol ne "") { @@ -4787,7 +4873,7 @@ sub ftp_mgr $password =~ s/('+)/'\"$1\"'/g; $home_path =~ s/('+)/'\"$1\"'/g; - if(!defined($Cfg::Preferences{ogp_manages_ftp}) || (defined($Cfg::Preferences{ogp_manages_ftp}) && $Cfg::Preferences{ogp_manages_ftp} eq "1")){ + if(preference_enabled('gsp_manages_ftp', 'ogp_manages_ftp', '1')){ if( defined($Cfg::Preferences{ftp_method}) && $Cfg::Preferences{ftp_method} eq "IspConfig") { use constant ISPCONFIG_DIR => Path::Class::Dir->new(AGENT_RUN_DIR, 'IspConfig'); @@ -5487,17 +5573,23 @@ if [ -n "\$POST_UPDATE" ]; then fi log "copy complete archive=\$ARCHIVE" sleep 2 +if command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files 2>/dev/null | grep -q '^gsp_agent\\.service'; then + systemctl restart gsp_agent.service >> "\$LOG" 2>&1 && exit 0 +fi if command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files 2>/dev/null | grep -q '^ogp_agent\\.service'; then systemctl restart ogp_agent.service >> "\$LOG" 2>&1 && exit 0 fi cd "\$DEST" || exit 0 +if [ -f gsp_agent_run.pid ]; then kill "\$(cat gsp_agent_run.pid)" >/dev/null 2>&1 || true; fi if [ -f ogp_agent_run.pid ]; then kill "\$(cat ogp_agent_run.pid)" >/dev/null 2>&1 || true; fi if [ -f ogp_agent.pid ]; then kill "\$(cat ogp_agent.pid)" >/dev/null 2>&1 || true; fi sleep 2 -if [ -f ogp_agent_run ]; then - screen -d -m -t "ogp_agent" -c $screenrc_q -S ogp_agent bash ogp_agent_run -pidfile ogp_agent_run.pid >> "\$LOG" 2>&1 || true +if [ -f gsp_agent_run ]; then + screen -d -m -t "gsp_agent" -c $screenrc_q -S gsp_agent bash gsp_agent_run -pidfile gsp_agent_run.pid >> "\$LOG" 2>&1 || true +elif [ -f ogp_agent_run ]; then + screen -d -m -t "gsp_agent" -c $screenrc_q -S gsp_agent bash ogp_agent_run -pidfile gsp_agent_run.pid >> "\$LOG" 2>&1 || true else - screen -d -m -t "ogp_agent" -c $screenrc_q -S ogp_agent perl ogp_agent.pl >> "\$LOG" 2>&1 || true + screen -d -m -t "gsp_agent" -c $screenrc_q -S gsp_agent perl ogp_agent.pl >> "\$LOG" 2>&1 || true fi log "restart attempted" rm -rf "\$TMP" @@ -5528,9 +5620,10 @@ sub agent_restart if ($dec_check eq 'restart') { chdir AGENT_RUN_DIR; - if(-e "ogp_agent_run.pid") + if(-e "gsp_agent_run.pid" || -e "ogp_agent_run.pid") { - my $init_pid = `cat ogp_agent_run.pid`; + my $run_pid_file = -e "gsp_agent_run.pid" ? "gsp_agent_run.pid" : "ogp_agent_run.pid"; + my $init_pid = `cat $run_pid_file`; chomp($init_pid); if(kill 0, $init_pid) @@ -5549,13 +5642,13 @@ sub agent_restart } open (AGENT_RESTART_SCRIPT, '>', 'tmp_restart.sh'); - my $restart = "echo -n \"Stopping OGP Agent...\"\n". + my $restart = "echo -n \"Stopping GSP Agent...\"\n". "kill $init_pid\n". "while [ -e /proc/$init_pid $or_exist ];do echo -n .;sleep 1;done\n". - "rm -f ogp_agent_run.pid $rm_pid_file\necho \" [OK]\"\n". - "echo -n \"Starting OGP Agent...\"\n". - "screen -d -m -t \"ogp_agent\" -c \"" . SCREENRC_FILE . "\" -S ogp_agent bash ogp_agent_run -pidfile ogp_agent_run.pid\n". - "while [ ! -e ogp_agent_run.pid -o ! -e ogp_agent.pid ];do echo -n .;sleep 1;done\n". + "rm -f gsp_agent_run.pid ogp_agent_run.pid $rm_pid_file\necho \" [OK]\"\n". + "echo -n \"Starting GSP Agent...\"\n". + "screen -d -m -t \"gsp_agent\" -c \"" . SCREENRC_FILE . "\" -S gsp_agent bash gsp_agent_run -pidfile gsp_agent_run.pid\n". + "while [ ! -e gsp_agent_run.pid -o ! -e ogp_agent.pid ];do echo -n .;sleep 1;done\n". "echo \" [OK]\"\n". "rm -f tmp_restart.sh\n". "exit 0\n"; diff --git a/ogp_agent_run b/ogp_agent_run old mode 100644 new mode 100755 index 371e329..fecb8f8 --- a/ogp_agent_run +++ b/ogp_agent_run @@ -1,411 +1,5 @@ -#!/bin/bash -# -# -# A wrapper script for the OGP agent perl script. -# Performs auto-restarting of the agent on crash. You can -# extend this to log crashes and more. -# -# The ogp_agent_run script should be at the top level of the agent tree -# Make sure we are in that directory since the script assumes this is the case +#!/usr/bin/env bash -##################### -# Important VARS # -##################### - -AGENTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -BASH_PREFS_CONF="$AGENTDIR/Cfg/bash_prefs.cfg" -REPONAME=OGP-Agent-Linux -GitHubUsername="OpenGamePanel" - -##################### -# FUNCTIONS # -##################### - -setupOGPDirPerms(){ - - chmod -Rf ug+rw $AGENTDIR 2>/dev/null - if [ -d "$AGENTDIR/steamcmd" ]; then - chmod ug+x $AGENTDIR/steamcmd/linux32/* 2>/dev/null - chmod ug+x $AGENTDIR/steamcmd/*.sh 2>/dev/null - fi - if [ -d "$AGENTDIR/screenlogs" ]; then - chmod -Rf ug=rwx $AGENTDIR/screenlogs - fi - chmod ug+x $AGENTDIR/ogp_agent.pl 2>/dev/null - chmod ug+x $AGENTDIR/ogp_agent_run 2>/dev/null - chmod ug+x $AGENTDIR/agent_conf.sh 2>/dev/null - - if test `id -u` -eq 0; then - echo - echo - echo "************** WARNING ***************" - echo "Running OGP's agent as root " - echo "is highly discouraged. It is generally" - echo "unnecessary to use root privileges to " - echo "execute the agent. " - echo "**************************************" - echo - echo - timeout=10 - while test $timeout -gt 0; do - echo -n "The agent will continue to launch in $timeout seconds\r" - timeout=`expr $timeout - 1` - sleep 1 - done - fi -} - -ogpGitCleanup(){ - echo "Cleaning up..." - rm -Rf ${REPONAME}-* &> /dev/null - if [ -e "ogp_agent_latest.zip" ]; then - rm -f "ogp_agent_latest.zip" - fi -} - -getSudoPassword(){ - sudoPass=$(cat "$AGENTDIR/Cfg/Config.pm" | grep -o "sudo_password.*" | grep -ow "[^sudo_password( \)*=>( \)*].*" | grep -o "[^'].*[^',]") -} - -detectSystemD(){ - replaceSystemDService=false - # Ops require sudo - if [ ! -z "$sudoPass" ]; then - initProcessStr=$(ps -p 1 | awk '{print $4}' | tail -n 1) - if [ "$initProcessStr" == "systemd" ]; then - systemdPresent=1 - if [ -e "/lib/systemd/system" ]; then - SystemDDir="/lib/systemd/system" - elif [ -e "/etc/systemd/system" ]; then - SystemDDir="/etc/systemd/system" - else - checkDir=$(ps -eaf|grep '[s]ystemd' | head -n 1 | awk '{print $8}' | grep -o ".*systemd/") - if [ -e "${checkDir}system" ]; then - SystemDDir="$checkDir" - else - # Can't find systemd dir - systemdPresent= - SystemDDir= - fi - fi - fi - - if [ -e "${AGENTDIR}/systemd/ogp_agent.service" ]; then - if [ ! -z "$systemdPresent" ] && [ ! -z "$SystemDDir" ]; then - echo -e "systemd detected as the init system with a directory of $SystemDDir." - if [ -e "/etc/init.d/ogp_agent" ] && [ ! -e "${AGENTDIR}/ogp_agent_init" ]; then - echo -e "Taking care of existing OGP files." - # Kill any remaining ogp agent process - ogpPID=$(ps -ef | grep -v grep | grep ogp_agent.pl | head -n 1 | awk '{print $3}') - if [ ! -z "$ogpPID" ]; then - echo "$sudoPass" | sudo -S -p "" kill -9 "$ogpPID" - fi - echo "$sudoPass" | sudo -S -p "" cp "/etc/init.d/ogp_agent" "${AGENTDIR}/ogp_agent_init" - echo "$sudoPass" | sudo -S -p "" chmod +x "${AGENTDIR}/ogp_agent_init" - echo "$sudoPass" | sudo -S -p "" update-rc.d ogp_agent disable - echo "$sudoPass" | sudo -S -p "" chkconfig ogp_agent off - echo "$sudoPass" | sudo -S -p "" rm -rf "/etc/init.d/ogp_agent" - replaceSystemDService=true - fi - - # Update service to use oneshot and not forking - if [ -e "$SystemDDir/ogp_agent.service" ]; then - # Check to see if it's using oneshot - usingOneShot=$(echo "$sudoPass" | sudo -S -p "" cat "$SystemDDir/ogp_agent.service" | grep -o "oneshot") - if [ -z "$usingOneShot" ]; then - replaceSystemDService=true - fi - fi - - if [ ! -e "$SystemDDir/ogp_agent.service" ] || [ "$replaceSystemDService" = true ]; then - echo -e "Updating OGP agent systemd service init script." - echo -e "Copying ogp_agent systemd service file to $SystemDDir" - echo "$sudoPass" | sudo -S -p "" cp "${AGENTDIR}/systemd/ogp_agent.service" "$SystemDDir" - echo "$sudoPass" | sudo -S -p "" sed -i "s#{OGP_AGENT_PATH}#$AGENTDIR#g" "${SystemDDir}/ogp_agent.service" - echo "$sudoPass" | sudo -S -p "" systemctl daemon-reload - echo "$sudoPass" | sudo -S -p "" systemctl enable ogp_agent.service - echo "$sudoPass" | sudo -S -p "" service ogp_agent restart - exit 0 - fi - fi - fi - fi -} - -init() { - RESTART="yes" - AGENT="$AGENTDIR/ogp_agent.pl" - TIMEOUT=10 # time to wait after a crash (in seconds) - PID_FILE="" - - # Should we perform an automatic update? - if [ -e $BASH_PREFS_CONF ] - then - source "$BASH_PREFS_CONF" - - if [ "$agent_auto_update" -eq "1" ] - then - AUTO_UPDATE="yes" - fi - - # Use custom github update address - if [ ! -z "$github_update_username" ]; then - REVISIONTest=`curl -s https://github.com/${github_update_username}/${REPONAME}/commits/master.atom | egrep -o "([a-f0-9]{40})" | awk 'NR==1{print $1}'` - if [ ! -z "$REVISIONTest" ]; then - GitHubUsername=${github_update_username} - fi - fi - - else - AUTO_UPDATE="yes" - fi - - while test $# -gt 0; do - case "$1" in - "-pidfile") - PID_FILE="$2" - PID_FILE_SET=1 - echo $$ > $PID_FILE - shift ;; - esac - shift - done - - if test ! -f "$AGENT"; then - echo "ERROR: '$AGENT' not found, exiting" - quit 1 - elif test ! -x "$AGENT"; then - # Could try chmod but dont know what we will be - # chmoding so just fail. - echo "ERROR: '$AGENT' not executable, exiting" - quit 1 - fi - - CMD="perl $AGENT" -} - -syntax () { - # Prints script syntax - echo "Syntax:" - echo "$0" -} - -checkDepends() { - CURL=`which curl 2>/dev/null` - if test "$?" -gt 0; then - echo "WARNING: Failed to locate curl binary." - else - echo "INFO: Located curl: $CURL" - fi - UNZIP=`which unzip 2>/dev/null` - if test "$?" -gt 0; then - echo "WARNING: Failed to locate unzip binary." - else - echo "INFO: Located unzip: $UNZIP" - fi -} - -update() { - # Check to see if limited ogpuser exists - generateOGPLimitedUser - - # Run the update - if test -n "$AUTO_UPDATE"; then - if [ -z "$CURL" -o -z "$UNZIP" ]; then - checkDepends - fi - if [ -f "$CURL" -a -x "$CURL" ] && [ -f "$UNZIP" -a -x "$UNZIP" ]; then - cd $AGENTDIR - if [ ! -d tmp ]; then - mkdir tmp - fi - cd tmp - REVISION=`curl -s https://github.com/${GitHubUsername}/${REPONAME}/commits/master.atom | egrep -o "([a-f0-9]{40})" | awk 'NR==1{print $1}'` - curl -Os https://raw.githubusercontent.com/${GitHubUsername}/${REPONAME}/${REVISION}/ogp_agent_run - currentOGPAgentRunContent=$(cat "./ogp_agent_run") - # Check to make sure ogp_agent_run downloaded successfully from GitHub before we attempt to replace it. - # This should fix random 404 people have been experiencing - if [ -s "./ogp_agent_run" ] && [ "$(echo "$currentOGPAgentRunContent" | head -n 1)" != "404: Not Found" ] && [ ! -z "$(echo "$currentOGPAgentRunContent" | grep "ogp_agent.pl")" ]; then - diff ./ogp_agent_run $AGENTDIR/ogp_agent_run &>/dev/null - if test $? -ne 0; then - cp -f ./ogp_agent_run $AGENTDIR/ogp_agent_run &> /dev/null - if test $? -eq 0; then - cd $AGENTDIR - chmod ug+x ogp_agent_run 2>/dev/null - echo "`date`: The agent updater has been changed, relaunching..." - rm -Rf tmp - ./ogp_agent_run - exit 0 - fi - fi - fi - cd $AGENTDIR - CURRENT=$(cat $AGENTDIR/Cfg/Config.pm | grep version | grep -Eo '[0-9a-f]{40}') - if [ "$CURRENT" == "$REVISION" ]; then - echo "The agent is up to date." - else - URL=https://github.com/${GitHubUsername}/${REPONAME}/archive/${REVISION}.zip - HEAD=$(curl -L -s --head -w "%{http_code}" "$URL" -o "ogp_agent_latest.zip") - if [ "$HEAD" == "200" ]; then - echo "Updating agent using curl." - curl -L -s "$URL" -o "ogp_agent_latest.zip" - if test $? -ne 0; then - echo "`date`: curl failed to download the update package." - else - unzip -oq "ogp_agent_latest.zip" - if test $? -ne 0; then - echo "`date`: Unable to unzip the update package." - ogpGitCleanup - else - cd ${REPONAME}-${REVISION} - cp -avf systemd Frontier ArmaBE Minecraft Schedule Time FastDownload php-query ogp_agent.pl ogp_screenrc ogp_screenrc_bk ogp_agent_run agent_conf.sh $AGENTDIR &> /dev/null - if test $? -ne 0; then - echo "`date`: The agent files cannot be overwritten." - cd .. - ogpGitCleanup - echo "Agent update failed." - else - if test ! -d "$AGENTDIR/IspConfig"; then - cp -Rf IspConfig $AGENTDIR/IspConfig &> /dev/null - fi - if test ! -d "$AGENTDIR/EHCP"; then - cp -Rf EHCP $AGENTDIR/EHCP &> /dev/null - fi - if test ! -f "$AGENTDIR/Cfg/Preferences.pm"; then - cp -f Cfg/Preferences.pm $AGENTDIR/Cfg/Preferences.pm &> /dev/null - fi - echo "Fixing permissions..." - chmod ug+x $AGENTDIR/ogp_agent.pl &> /dev/null - chmod ug+x $AGENTDIR/ogp_agent_run &> /dev/null - chmod ug+x $AGENTDIR/agent_conf.sh &> /dev/null - cd .. - ogpGitCleanup - sed -i "s/version.*/version => '${REVISION}',/" $AGENTDIR/Cfg/Config.pm - echo "Agent updated successfully." - fi - fi - fi - else - echo "There is a update available (${REVISION}) but the download source is not ready."; - echo "Try again later." - fi - fi - else - echo "Update failed." - fi - fi - return 0 -} - -run() { - getSudoPassword - restrictAccess - update - detectSystemD - if test -n "$RESTART" ; then - echo "Agent will auto-restart if there is a crash." - #loop forever - while true - do - # Run - $CMD - echo "`date`: Agent restart in $TIMEOUT seconds" - # don't thrash the hard disk if the agent dies, wait a little - sleep $TIMEOUT - done # while true - else - $CMD - fi -} - -quit() { - # Exits with the give error code, 1 - # if none specified. - # exit code 2 also prints syntax - exitcode="$1" - - # default to failure - if test -z "$exitcode"; then - exitcode=1 - fi - - case "$exitcode" in - 0) - echo "`date`: OGP Agent Quit" ;; - 2) - syntax ;; - *) - echo "`date`: OGP Agent Failed" ;; - esac - - # Remove pid file - if test -n "$PID_FILE" && test -f "$PID_FILE" ; then - # The specified pid file - rm -f $PID_FILE - fi - - # reset SIGINT and then kill ourselves properly - trap - 2 - kill -2 $$ -} - -function generatePassword(){ - if [ ! -z "$1" ]; then - PLENGTH="$1" - else - PLENGTH="10" - fi - - #rPass=$(date +%s | sha256sum | base64 | head -c "$PLENGTH") - rPass=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1 | head -c "$PLENGTH") -} - -function generateOGPLimitedUser(){ - ogpUSER="ogp_server_runner" - echo "$sudoPass" | sudo -S -p "" id -u "$ogpUSER" - if [ $? -eq 1 ]; then - echo "Creating ogp limited user for running servers and additional security..." - echo "$sudoPass" | sudo -S -p "" groupdel ${ogpUSER} - echo "$sudoPass" | sudo -S -p "" groupadd ${ogpUSER} - generatePassword "15" - ogpPass="$rPass" - echo "$sudoPass" | sudo -S -p "" useradd --home "/home/$ogpUSER" -g ${ogpUSER} -m "$ogpUSER" - echo "$sudoPass" | sudo -S -p "" sh -c "echo '${ogpUSER}:${ogpPass}' | chpasswd" - # Use /bin/bash shell by default per request from Omano - echo "$sudoPass" | sudo -S -p "" usermod -s /bin/bash ${ogpUSER} - echo "$sudoPass" | sudo -S -p "" sh -c "echo 'limited_ogp_user=${ogpUSER} -password=${ogpPass}' > /root/ogp_server_runner_info" - agentUser="$(whoami)" - echo "$sudoPass" | sudo -S -p "" usermod -a -G "$ogpUSER" "$agentUser" - # Reload perms so we don't have to reboot system for new group to apply right now... - echo "$sudoPass" | sudo -S -p "" exec su -l "$agentUser" - fi - - # Set servers to run under their own user by default: - hasLinuxUser=$(cat "$AGENTDIR/Cfg/Preferences.pm" | grep -o "linux_user_per_game_server") - if [ -z "$hasLinuxUser" ]; then - sed -i "\$i \\\\tlinux_user_per_game_server => '1'," "$AGENTDIR/Cfg/Preferences.pm" - fi -} - -function restrictAccess(){ - echo "$sudoPass" | sudo -S -p "" chmod 750 "$AGENTDIR/Cfg/Config.pm" - echo "$sudoPass" | sudo -S -p "" chmod 750 "$AGENTDIR/Cfg/Preferences.pm" - echo "$sudoPass" | sudo -S -p "" chmod 750 "$AGENTDIR/Cfg/bash_prefs.cfg" -} - -##################### -# MAIN APP CODE # -##################### - -# Setup OGP and Read Preferences -setupOGPDirPerms - -# Initialise -init $* - -# Run -run - -# Quit normally -quit 0 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +echo "ogp_agent_run is a legacy compatibility wrapper. Use gsp_agent_run for new GSP installs." >&2 +exec "${SCRIPT_DIR}/gsp_agent_run" "$@" diff --git a/ogp_screenrc b/ogp_screenrc index 5cb82eb..32db447 100644 --- a/ogp_screenrc +++ b/ogp_screenrc @@ -4,5 +4,4 @@ hardstatus alwayslastline '%{gk}[ %{G}%H %{g}][%= %{wk}%?%-Lw%?%{=b kR}[%{W}%n%f # Default scroll back 100 defscrollback 100 deflog on -logfile /home/gameserver/OGP/screenlogs/screenlog.%t - +logfile $PWD/screenlogs/screenlog.%t diff --git a/resource_usage_test.pl b/resource_usage_test.pl index 40d1393..88cd5c6 100644 --- a/resource_usage_test.pl +++ b/resource_usage_test.pl @@ -127,6 +127,6 @@ print "To enable it:\n"; print "1. Configure database settings in Cfg/Config.pm\n"; print "2. Import SQL schema files from DB/ directory\n"; print "3. Install DBI and DBD::mysql Perl modules\n"; -print "4. Restart the OGP Agent\n"; +print "4. Restart the GSP Agent\n"; print "\nThe system will automatically start collecting resource data\n"; print "every " . ($Cfg::Config{stats_frequency_minutes} || '5') . " minutes when enabled.\n"; diff --git a/systemd/gsp_agent.service b/systemd/gsp_agent.service new file mode 100644 index 0000000..f693391 --- /dev/null +++ b/systemd/gsp_agent.service @@ -0,0 +1,20 @@ +# GSP Linux Agent systemd service + +[Unit] +Description=GameServer Panel Linux Agent +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User={GSP_AGENT_USER} +Group={GSP_AGENT_USER} +WorkingDirectory={GSP_AGENT_PATH} +ExecStart={GSP_AGENT_RUN_SCRIPT} -pidfile {GSP_AGENT_PID_FILE} +Restart=on-failure +RestartSec=10 +KillSignal=SIGTERM +TimeoutStopSec=60 + +[Install] +WantedBy=multi-user.target diff --git a/systemd/ogp_agent.service b/systemd/ogp_agent.service index 7bf32e5..5f8761f 100644 --- a/systemd/ogp_agent.service +++ b/systemd/ogp_agent.service @@ -1,16 +1,18 @@ -# ogp_agent systemd Service Script - +# Legacy compatibility unit. New installs should use gsp_agent.service. [Unit] -Description=GameServer Panel (GSP) Daemon www.worlddomination.dev -After=network.target +Description=GameServer Panel Linux Agent legacy service alias +After=network-online.target +Wants=network-online.target [Service] -Type=oneshot -ExecStart=/bin/sh -c "{OGP_AGENT_PATH}/ogp_agent_init start" -ExecStop=/bin/sh -c "{OGP_AGENT_PATH}/ogp_agent_init stop" -RemainAfterExit=yes -PIDFile="{OGP_AGENT_PATH}/ogp_agent_run.pid" +Type=simple +User={GSP_AGENT_USER} +Group={GSP_AGENT_USER} +WorkingDirectory={GSP_AGENT_PATH} +ExecStart={GSP_AGENT_PATH}/gsp_agent_run -pidfile {GSP_AGENT_PATH}/gsp_agent_run.pid +Restart=on-failure +RestartSec=10 [Install] WantedBy=multi-user.target