FedoraThis forum is for the discussion of the Fedora Project.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I have a dedicated box that is used to run several game servers. Each server has its own seperate start script. Now the problem that I seem to be having is that if the box crashes and restarts itself or if it is rebooted my me, all of the start scripts for my game servers are renamed and an empty one with the same name is put in its place...for example:
the script gamespro in /etc/rc.d/init.d/ is renamed to gamespro.1 and an empty file called gamespro is created
the strange thing is the gameservers do startup when the box restarts, so theyre apparently getting renamed sometime AFTER the gameservers are booted at startup. Anyone know whats causing this? Webmin is my admin panel for the server if that makes a difference.
Alrighty, well this is one of 4 scripts, all of them are the same except that they start a different server:
in /etc/rc.d/init.d/
If allowed to boot on startup, this is the one that is always renamed from games to games.1 and an empty file called games is put in its place.
Code:
#!/usr/bin/perl
# gameserver startup script init file
#
# chkconfig: 2345 99 99
# description: Game Manager
#
# config: /etc/games/configs
#
# Red Hat run 'chkconfig --add games' to add this init script to the system
#
# ** now that the red hat crud is taken care of **
# Name: Game Server Startup Script
# Description: Perl script to manage linux dedicated game servers
# Version: 1.1
# Authors: Jason Jorgensen <jasonj@innominatus.com>
# Copyright (C) 2003 Jason Jorgensen
#
# 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
# (at your option) 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
###########################################################################
#
# Dependencies: - must have qstat install for verification that a game is running like its suppose to be.
# - 'netstat -lep' must stay the same from when this was developed, else the &get_whats_running() function must be modified
my $debug = 0;
my $gameserver_user = "fragsolution";
my $configdir = "/etc/games/configs";
my $chrootdir = "/usr/local/games";
my $prevbroken_file = "/tmp/previously_broken_game_servers";
my ($scriptname) =( $0 =~ /.*\/(.*)$/ );
my $gameserver_username;
my $config_dir;
my @selected;
my $arg1;
my $qstatbinary = `which qstat`;
chomp $qstatbinary;
my @options = ('start', 'stop', 'restart', 'list');
if ($qstatbinary) {push @options, ('cronfix', 'fix', 'status'); }
my @configs;
my @unsortedallconfigfiles; #
my @allconfigfiles; #
my @allports; # All derived from @configs
my @allprocessnames; #
my @allpids; #
my %validports;
&configs_setup(); #populate the above config specific variables
my @runninggames;
my @allrunningports; #
my @allrunningprocessnames; # All derived from @runninggames
my @allrunningpids; #
&running_games_setup(); #populate the above running game specific variables
my %pids;
&correlate_pids();
if ($debug) {
foreach my $key (keys %pids) {
print "$key: $pids{$key}\n";
}
}
#main
my $numberofargs = $#ARGV;
if ($numberofargs > -1) {
$arg1 = shift @ARGV;
}
if ($numberofargs > 0) {
@selected = &set_selected(@ARGV);
}
else { @selected = @configs; }
CASE: {
if ($arg1 =~ /^start$/i) {
print "Starting games\n";
&start(@selected);
last CASE;
}
if ($arg1 =~ /^stop$/i) {
print "Stopping games\n";
&stop(@selected);
last CASE;
}
if ($arg1 =~ /^restart$/i) {
print "Restarting games\n";
&stop(@selected);
sleep 4;
&running_games_setup();
&correlate_pids();
&start(@selected);
last CASE;
}
if ($arg1 =~ /^cronfix$/i) {
(my $brokenpids, my @brokenservers) = &whats_broke(); #find out whats broken and return a scalar of bad pids and a list of the servers that are down
# my $env = `printenv`;
# print "$env\n";
print "DEBUG: broken pids: $brokenpids brokenservers: @brokenservers\n" if $debug;
if (@brokenservers) {
my @prevbrokenservers = &read_prevbroken();
foreach my $brokenserver (@brokenservers) {
if (grep /$brokenserver/, @prevbrokenservers) {
print "Cron Job, automatically fixing game servers\n";
print "Listing all broken games: \n";
print " ".join("\n ", @brokenservers)."\n";
@selected = &set_selected(@brokenservers);
&stop(@selected);
sleep 4;
&running_games_setup();
&correlate_pids();
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin/X11:./:/usr/X11R6/bin:/usr/local/bin:/root/bin:/usr/local/bin";
&start(@selected);
}
else { &write_prevbroken(@brokenservers); }
}
}
last CASE;
}
if ($arg1 =~ /^fix$/i) {
print "Fixing games: \n";
(my $brokenpids, my @brokenservers) = &whats_broke(); #find out whats broken and return a scalar of bad pids and a list of the servers that are down
print "Listing all broken games: \n";
print " ".join("\n ", @brokenservers)."\n";
@selected = &set_selected(@brokenservers);
&stop(@selected);
sleep 4;
&running_games_setup();
&correlate_pids();
&start(@selected);
last CASE;
}
if ($arg1 =~ /^status$/i) {
print "Status of games: \n";
&status();
last CASE;
}
if ($arg1 =~ /^list$/i) {
print "Listing all games: \n";
print " ".join("\n ", @allconfigfiles)."\n";
last CASE;
}
print "Usage: /etc/init.d/$scriptname {".join("|",@options)."}\n";
print " With 'start|stop|restart' you can specify specific servers from 'list'\n";
}
exit 0;
sub start {
my @selected = @_;
foreach my $config (@selected) {
print "DEBUG: startpath: $config->{'startpath'}\n" if $debug;
if (! $pids{$config->{'configfile'}}) {
print "Starting game: $config->{'configfile'}\n";
chdir $config->{'startpath'} || &error("Not a valid start path, can't start server"); # Change directories or bitch
print "DEBUG: system(\"su $gameserver_user -c '$config->{'binary'} $config->{'commonargs'} $config->{'args'}' &/dev/null &\")\n" if $debug;
my $errorcode = system("su $gameserver_user -c '$config->{'binary'} $config->{'commonargs'} $config->{'args'}' >/dev/null 2>&1 &") && die "couldnt start $config->{'configfile'}: $?\n"; # Start the game!
print "Return Code: $errorcode\n" if $debug;
}
else { &error("game is already running, choose restart"); }
}
}
sub stop {
my @selected = @_;
print "DEBUG: selected: @selected[0]->{'configfile'}\n" if $debug;
foreach my $config (@selected) {
print "Stopping game: $config->{'configfile'}\n";
print "DEBUG: pids to be killed: $pids{$config->{'configfile'}}\n" if $debug;
system("kill $pids{$config->{'configfile'}} >/dev/null 2>&1");
system("kill -9 $pids{$config->{'configfile'}} >/dev/null 2>&1");
}
}
sub status{
my $status = &qstat(@configs);
my @statuslines = split(/\n/, $status);
print " Config File Type IP/Port Server Name Map MP CP \n";
foreach my $line (@statuslines) {
if ($line =~ /::/) {
my @line = split /::/, $line;
if (@line[$#line] =~ /[DOWN|TIMEOUT]/) {
print swrite(<<'END', @line);
@<<<<<<<<<<<<<<<<<<< @<< @<<<<<<<<<<<<<<< @<<<<<<<<<<<<<< **** WARNING ****
END
}
else {
print swrite(<<'END', @line);
@<<<<<<<<<<<<<<<<<<< @<< @<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<< @<< @<< @<< @<<
END
}
}
else { print " $line\n"; }
}
}
sub set_selected {
my @selections = @_;
my @selected;
foreach my $selection (@selections) {
foreach my $config (@configs) {
if ($selection eq $config->{'configfile'}) { push @selected, $config; }
}
}
print "DEBUG: selected servers: @selected\n" if $debug;
return @selected;
}
sub configs_setup {
@configs = &read_configs();
undef @unsortedallconfigfiles; #
undef @allconfigfiles; #
undef @allports; # All derived from @configs
undef @allprocessnames; #
undef @allpids; #
undef %validports; #
foreach my $hashref (@configs) {
foreach my $key (keys %{$hashref}) {
if ($key eq 'configfile') { push @unsortedallconfigfiles, $hashref->{$key}; }
if ($key eq 'port') { push @allports, $hashref->{$key}; }
if ($key eq 'ports') { push @{$validports{$hashref->{'processname'}}}, (split /\ /,$hashref->{$key}); }
if ($key eq 'pid') { push @allpids, $hashref->{$key}; }
if ($key eq 'processname') { push @allprocessnames, $hashref->{$key}; }
}
}
if ($debug) {
print "Valid Ports\n";
foreach my $process (keys %validports) {
print "$process: @{$validports{$process}}\n";
}
}
@allconfigfiles = sort @unsortedallconfigfiles;
}
sub running_games_setup {
@runninggames = &get_whats_running();
undef @allrunningports; #
undef @allrunningprocessnames; # All derived from @runninggames
undef @allrunningpids; #
foreach my $hashref (@runninggames) {
foreach my $key (keys %{$hashref}) {
if ($key eq 'port') { push @allrunningports, $hashref->{$key}; }
if ($key eq 'pid') { push @allrunningpids, $hashref->{$key}; }
if ($key eq 'processname') { push @allrunningprocessnames, $hashref->{$key}; }
}
}
}
sub correlate_pids {
undef %pids;
foreach my $runninggame (@runninggames) {
foreach my $config (@configs) {
print "DEBUG: equal?: $runninggame->{'processname'} eq $config->{'processname'}) && ($runninggame->{'port'} == $config->{'port'})) { \$pids{$config->{'configfile'}} = $runninggame->{'pid'};\n" if $debug;
if (($runninggame->{'processname'} eq $config->{'processname'}) && ($runninggame->{'port'} == $config->{'port'})) { $pids{$config->{'configfile'}} = $runninggame->{'pid'}; }
print "DEBUG: pids for $config->{'configfile'}: $pids{$config->{'configfile'}} \n" if $debug;
}
}
}
sub get_whats_running {
my $line;
my @splittitles;
my @games;
my @cols;
open (NETSTAT, "netstat -lep 2>/dev/null |");
while (<NETSTAT>) {
$line = $_;
if ($line =~ /.*Proto.*/) {
@splittitles = ($line =~ /([^ ]* name|[^ ]* Address|[^ ]*) +/g); # get all the titles of the output table (thanks to EE)
#@splittitles = split /\s+/, $line; # get all the titles of the output table (old and broken)
foreach my $title (@splittitles) {
push @cols, (index $line, $title); # get all the starting positions for all the titles in the table
}
print "DEBUG: \@cols: @cols\n" if $debug;
my $counter = 0;
if ($debug) {
foreach my $element (@splittitles) {
print $counter.":".$element."\t";
$counter++;
}
print "\n";
}
shift @cols;
}
if ($line =~ /.*$gameserver_user.*/){
my @splitline;
my $prev = 0;
foreach my $col (@cols, (length $line)) {
my $temp = substr($line, $prev, $col - $prev);
$temp =~ s/^\s+//;
$temp =~ s/\s+$//;
push @splitline, $temp;
$prev = $col;
}
if ($debug) {
my $counter = 0;
foreach my $element (@splitline) {
print $counter.":".$element."\t";
$counter++;
}
print "\n";
}
my $game;
(my $crap, my $port) = split /:/, $splitline[3];
(my $pid, my $processname) = split /\//, $splitline[8];
# print "DEBUG: if (grep /^$processname$/, @allprocessnames) {\n";
if (grep /^$processname$/, @allprocessnames) {
$game->{'pid'} = $pid;
$game->{'port'} = $port;
$game->{'processname'} = $processname;
push @games, $game;
}
}
}
close NETSTAT;
if ($debug) {
foreach my $hashref (@games) {
print "DEBUG: ============================================================\n";
foreach my $key (keys %{$hashref}) {
print "DEBUG: $key => $hashref->{$key}\n";
}
}
}
return @games;
}
sub read_prevbroken {
my @prevbroken; # array of hashrefs containing the data from all the valid config files
my $line;
open (PREVBROKE, "<$prevbroken_file");
while (<PREVBROKE>) {
$line = $_;
chomp $line;
if ($line !~ /^#/) { push @prevbroken, $line; }
}
close PREVBROKE;
print "DEBUG: previously broken game servers: @prevbroken\n" if $debug;
return @prevbroken;
}
sub write_prevbroken {
my @prevbroken = @_; # array of hashrefs containing the data from all the valid config files
my $line;
open (PREVBROKE, ">$prevbroken_file");
foreach my $brokenserver (@prevbroken) {
print PREVBROKE $brokenserver."\n";
}
close PREVBROKE;
print "DEBUG: previously broken game servers: @prevbroken\n" if $debug;
}
sub read_configs {
my @configfiles; # array of config file names
my $configfile; # single filename
my @configs; # array of hashrefs containing the data from all the valid config files
my $variable; # unused variable used for split
my $temp; # used for a short time before populating the final variable for that data
my @temp;
opendir(CONFIGDIR, "$configdir");
@configfiles = grep !/^\./, readdir CONFIGDIR;
closedir CONFIGDIR;
foreach $configfile (@configfiles) {
my $config; # single hashref containing the data from a valid config file
my @invalid;
open (FILE, "$configdir/$configfile");
while (<FILE>) {
chomp;
print "DEBUG: current config line: $_\n" if $debug;
($variable, @temp) = split /=/, $_;
$temp = join '=', @temp;
$temp =~ s/^[\"|\']//;
$temp =~ s/[\"|\']$//;
SWITCH: {
if ($variable =~ /^STARTPATH$/) { $config->{'startpath'} = $temp; last SWITCH; }
if ($variable =~ /^BINARY$/) { $config->{'binary'} = $temp; last SWITCH; }
if ($variable =~ /^PROCESSNAME$/) { $config->{'processname'} = $temp; last SWITCH; }
if ($variable =~ /^PORT$/) { $config->{'port'} = $temp; last SWITCH; }
if ($variable =~ /^PORTS$/) { $config->{'ports'} = $temp; last SWITCH; }
if ($variable =~ /^COMMONARGS$/) { $config->{'commonargs'} = $temp; last SWITCH; }
if ($variable =~ /^ARGS$/) { $config->{'args'} = $temp; last SWITCH; }
if ($variable =~ /^QSTAT$/) { $config->{'qstat'} = $temp; last SWITCH; }
}
}
$config->{'configfile'} = $configfile;
print "DEBUG: configfile: $config->{'configfile'} startpath: $config->{'startpath'} binary:$config->{'binary'} processname:$config->{'processname'} ports:$config->{'ports'}\n" if $debug;
if (! $config->{'startpath'}) { push @invalid, "No STARTPATH specified"; }
if (! $config->{'binary'} ) { push @invalid, "No BINARY specified"; }
if (! $config->{'processname'} ) { push @invalid, "No PROCESSNAME specified"; }
if (! $config->{'port'} ) { push @invalid, "No PORT specified"; }
if (! $config->{'ports'} ) { push @invalid, "No PORTS specified"; }
if (! @invalid) { push @configs, $config; }
else {
&error("$configdir/$configfile is an invalid config file and will be ignored");
foreach my $invalidresponse (@invalid) {
&error(" $invalidresponse");
}
}
}
close FILE;
if ($debug) {
foreach my $hashref (@configs) {
print "DEBUG: ============================================================\n";
foreach my $key (keys %{$hashref}) {
print "DEBUG: $key => $hashref->{$key}\n";
}
}
}
return @configs;
}
sub whats_broke {
my $downserverpids;
my @downservers;
foreach my $game (@configs) {
if ($game->{'qstat'}) {
print "DEBUG: qstat call: $qstatbinary -raw :: -$game->{'qstat'} 127.0.0.1:$game->{'port'}\n"if $debug;
my $result = &qstat($game);
chomp $result;
print "DEBUG: result: $result\n" if $debug;
my @splitresult = split /::/, $result;
if ($splitresult[$#splitresult] =~ /DOWN.*/ || $splitresult[$#splitresult] =~ /TIMEOUT.*/) {
foreach my $runninggame (@runninggames) {
print "DEUBG: (($game->{'port'} == $runninggame->{'port'}) && ($game->{'processname'} eq $runninggame->{'processname'}))\n" if $debug;
if (($game->{'port'} == $runninggame->{'port'}) && ($game->{'processname'} eq $runninggame->{'processname'})) {
$downserverpids .= $runninggame->{'pid'}." ";
}
}
push @downservers, $game->{'configfile'};
}
}
}
print "DEBUG: downservers: @downservers |downserverpids: $downserverpids\n" if $debug;
return $downserverpids, @downservers;
}
sub qstat {
my @selected = @_;
my $result;
foreach my $selection (@selected) {
$result .= "$selection->{'configfile'}::";
if ($selection->{'qstat'}) {
$result .= `$qstatbinary -raw :: -$selection->{'qstat'} 127.0.0.1:$selection->{'port'}`;
chomp $result;
}
else { $result .= "This game does not support qstat for status.\n"; }
}
return $result;
}
sub swrite {
my $format = shift;
$^A = "";
formline($format,@_);
return $^A;
}
sub error {
my $error = shift;
print "ERROR: $error\n";
}
This is the config it executes to actually start the server, I havent noticed this file being changed in any way, Just posting it to show the complete process.
in /etc/games/configs/
Code:
STARTPATH="/usr/local/games/enemy-territory"
BINARY="./etded.x86pub"
PROCESSNAME="etded.x86pub"
PORT="27960"
PORTS="27960"
COMMONARGS="+set net_port 27960 +set sv_punkbuster 1 +set fs_game etpub +exec server.cfg"
ARGS=""
QSTAT="woets"
#######################################################################################
# STARTPATH = path the BINARY should be executed from
# BINARY = executable that should be run to start the game
# PROCESSNAME = the name the game shows up as when doing a netstat -lep
# PORTS = the ports this game should be allowed to use
# COMMONARGS = arguments that may be common to other games of this type (optional)
# ARGS = arguments that may be specific to this instance of the game (optional)
#######################################################################################
Basically when you run the first script with the command /etc/rc.d/init.d/games start it will startup all configs in /etc/games/configs/
It works fine when executed manually, but always seems to be renamed if it is allowed to Start on Boot.
This file called also has the same problem of being renamed and an empty file put in its place if allowed to Start on Boot, it's from etadmin_mod which basically reads the gameserver log file and adds some ingame commands. Its a shell script called etadmin_mod.sh in /usr/local/games/enemy-territory/etadmin_mod/
Code:
#! /bin/sh
#######################################################################
# CONFIG:
# General note: If you specify more then one parameter or config files:
# use brackets. e.g. "-r -e" or "1.cfg 2.cfg"
# Choose your config file(s) (space seperated):
# Remember: The last can overwrite parameters of the prior configs.
# Useful for hoster to deactivate or fix parameters
# Also see ADMIN_CONFIG for overwriting.
CONFIGS="/usr/local/games/enemy-territory255/etadmin_mod/etc/etadmin.cfg"
# This config is ALWAYS loaded after the CONFIGS have been loaded
# (even after a !loadconfig or map configs).
ADMIN_CONFIG=""
#ADMIN_CONFIG="/usr/local/games/enemy-territory255/etadmin_mod/etc/admin.cfg"
# Further Options (space seperated):
# -e to deactivate the including of a "external" section in map specific configs
# -r to reset the external section on every new section block.
# -d to completly deactivate the use of external programs (
# -> ignores the [external] section in the config.
#
# Note: for game hosters:
# - To completly disable external commands, use -d and fix the tail binary in a second config.
# - To define a preset of external commands to use for the user, use 2 configs
# (one for the user and one for you) and define the [external] section in your config,
# as well as the tail binary. Then use -r -e, so the user can't reset, add or modify the commands.
OPTIONS=""
# CHANGE THIS to the location of the extracted mod:
BASEDIR=/usr/local/games/enemy-territory/etadmin_mod
# Set this to a unique string for every instanz of etadmin_mod
# running on your server.
INSTANZ_NAME=adminmodpub
#######################################################################
# You don't need to change anything below this line #
#######################################################################
cd $BASEDIR
PERL=`type -p perl`
PID=`ps axw|grep etadmin_mod.pl |grep "instanz_$INSTANZ_NAME" |grep -v grep | awk '{print $1}'`
case "$1" in
start)
echo -n "Starting etadmin_mod: "
# Adding admin config to the options
if (test -n "$ADMIN_CONFIG") then
OPTIONS="$OPTIONS --admin-config=$ADMIN_CONFIG"
fi
if (test -z "$PID") then
$PERL /usr/local/games/enemy-territory/etadmin_mod/bin/etadmin_mod.pl $OPTIONS instanz_$INSTANZ_NAME $CONFIGS >>/usr/local/games/enemy-territory/etadmin_mod/log/etadmin.log 2>&1 &
sleep 1
STATUS=`ps axw|grep "etadmin_mod.pl" | grep "instanz_$INSTANZ_NAME" |grep -v grep `
if (test -n "$STATUS") then
echo "OK! [$STATUS]"
else
echo "FAILED! [$STATUS] (See logfile log/etadmin.log for details!)"
fi
else
echo "FAILED! (etadmin_mod is already running)"
fi
;;
stop)
echo -n "Stopping etadmin_mod: "
if (test -n "$PID") then
# First determine tail pid
PID_TAIL=`ps axw|grep tail |grep etconsole.log |grep " $PID " |awk '{print $2}'`
# killing etadmin_mod
kill $PID
# Killing tail:
if (test -n "$PID_TAIL") then
kill $PID_TAIL
fi
echo "OK!"
else
echo "FAILED! (etadmin_mod isn't running)"
fi
;;
reload)
echo -n "Reloading etadmin_mod: "
if (test -n "$PID") then
kill -HUP $PID
echo "OK! ($PID)"
else
echo "FAILED! (etadmin_mod isn't running)"
fi
;;
status)
if (test -n "$PID") then
echo "Status: running"
else
echo "Status: not running"
fi
;;
restart)
$0 stop
# Need to sleep here, because etadmin_mod needs some seconds to shutdown
sleep 2
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart|reload|status}"
exit 1
esac
# Inform the caller not only verbosely and set an exit status.
exit 0
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.