zservrotation.pl

Post date: May 21, 2010 5:57:45 PM

A perl utility to create Zdaemon's zserv configuration files. Useful only on Unix/Linux servers.

You need to have Config::IniFiles installed. Just do it with the help of CPAN (or PPM if you have ActiveState Perl).

#!perl

#

# zservrotation.pl by shinobi.cl@gmail.com

#

use strict;

use Data::Dumper;

use Config::IniFiles;

use Switch;

print '--- Starting zservrotation.pl version 0.7 ---'.$/;

print 'Local time : ';

system ("date");

print 'UTC time : ';

system ("date -u");

my $Config_File = shift;

$Config_File = 'zservrotation.conf' unless defined $Config_File;

die 'ERROR: Configuration file "'.$Config_File.'" not found'.$/ if (!(-e $Config_File));

my $conf = new Config::IniFiles -file => $Config_File;

my %Servers = ();

my %Configs = ();

#--- First, we fill the server data

my @ServerList = $conf->GroupMembers("SERVER");

foreach my $Server (@ServerList) {

my ($dummy, $Server_Name) = split /\s+/, $Server;

my @Parameters = $conf->Parameters($Server);

foreach my $Parameter (@Parameters) {

$Servers{$Server_Name}{$Parameter} = unquote(trim($conf->val($Server, $Parameter)));

}

}

my @ConfigList = $conf->GroupMembers("DETAILS");

my @General_Parameters = $conf->Parameters("GENERAL");

foreach my $Detail (@ConfigList) {

my ($dummy, $Detail_Id) = split /\s+/, $Detail;

my @Parameters = $conf->Parameters($Detail);

#For each detailed config, we add all the "general" settings.

foreach my $General_Parameter (@General_Parameters) {

next if ($General_Parameter eq '!currentiteration'); # special parameter

my $Value;

if ($General_Parameter eq '!verbatim') {

my @Content = $conf->val("GENERAL", '!verbatim');

$Value = \@Content;

} else {

$Value = unquote(trim($conf->val("GENERAL", $General_Parameter)));

}

$Configs{$Detail_Id}{$General_Parameter} = $Value;

}

foreach my $Parameter (@Parameters) {

# Only !extrawads, hostname and motd are added to the general settings.

# The others are overrided by the 'detailed' property.

my $Value;

if ($Parameter eq '!verbatim') {

my @Content = $conf->val($Detail, '!verbatim');

$Value = \@Content;

} else {

$Value = unquote(trim($conf->val($Detail, $Parameter)));

}

switch ($Parameter) {

case '!verbatim' {

push @{$Configs{$Detail_Id}{'!verbatim'}}, @{$Value};

}

case /^!extrawads$|^hostname$|^motd$/ {

$Configs{$Detail_Id}{$Parameter} .= ' '.$Value;

}

else { # Property is overrided

$Configs{$Detail_Id}{$Parameter} = $Value;

}

}

}

}

my @Rotation_States = split /,/, $conf->val("GENERAL", "!currentiteration");

foreach (@Rotation_States) {

my ($Server, $Turn) = split /:/;

$Turn = trim($Turn);

$Turn = 1 if ($Turn !~ /^\d+$/); # Turn defaults to 1 if not a Natural

$Servers{trim($Server)}{'#Iteration'} = $Turn;

}

my @Rotation_Parameters = $conf->Parameters("ROTATION");

foreach my $Server (@Rotation_Parameters) {

# Depending on the current rotation, we assign a DETAIL config to a server.

my @Rotation_List = split /,/, $conf->val("ROTATION", $Server);

$Servers{$Server}{'#Config'} = trim($Rotation_List[$Servers{$Server}{'#Iteration'}-1]);

my $Next = $Servers{$Server}{'#Iteration'}+1;

$Next = 1 if $Next > scalar @Rotation_List;

$Servers{$Server}{'#Next'} = $Next;

}

#--- validate minimum parameters ----------------------------------------------#

# TODO: do it..

#--- zserv.cfg file generation ------------------------------------------------#

my @New_Iteration = ();

my @Server_Paths = ();

foreach my $Server (keys %Servers) {

if (!(-e $Servers{$Server}{'!path'})) {

die 'ERROR: Path "'.$Servers{$Server}{'!path'}.'" for server "'.$Server.'"" not found.'

}

my @Z = (); # zserv.cfg contents

# Some stuff that should be there just because.

push @Z, '/////////////////////////////////////////////////';

push @Z, '// ZDAEMON ROTATING SERVER '.$Server;

push @Z, '/////////////////////////////////////////////////';

push @Z, 'set cfg_activated "1"';

my %Properties = %{$Configs{ $Servers{$Server}{'#Config'} }};

foreach my $Property (keys %Properties) {

my $Value = $Properties{$Property};

# Some properties are not part of the zserv file. Those start with '!'

next if ($Property =~ /^!currentiteration$|^!iwad$|^cfg_activated$/);

switch ($Property) {

case '!rotationexecutable' {

if ($Value eq 'zserv') {

warn 'WARN: You have set !rotationexecutable=zserv. Be aware that all !rotationexecutables are killed at the end of each rotation.'.$/;

last;

}

}

case /^!basepath$|^!waddir$/ {

if (!(-e $Value)) {

die 'ERROR: Path "'.$Value.'" not found.'

}

}

case /^!extrawads$|^!mainwad$/ {

my @Wads = split /\s+/, $Value;

foreach (@Wads) {

my $Wad_File = $Properties{'!waddir'}.'/'.$_;

$Wad_File =~ s/\/+/\//g;

if (!(wadfile_exists($Wad_File))) {

die 'ERROR: Wadfile "'.$Wad_File.'" not found.'.$/;

}

}

}

case '!verbatim' {

my @Content = @{$Value};

push @Z, $_ foreach (@Content);

}

else {

push @Z, 'set '.$Property.' "'.$Value.'"';

}

}

}

my $Launcher =

$Servers{$Server}{'!path'}.'/'.$Properties{'!rotationexecutable'}.

' -waddir '.$conf->val("GENERAL", '!waddir').

' -iwad '.$Properties{'!iwad'}.

' -file '.$Properties{'!mainwad'}.' '.$Properties{'!extrawads'}.

' -port '.$Servers{$Server}{'!port'}.

' >> '.$conf->val("GENERAL", '!basepath').'/'.$Server.'.log &';

$Launcher =~ s/\/+/\//g;

# Launcher is written to runserver.sh

# @Z is written to zserv.cfg

open S_LAUNCHER, '>'.$Servers{$Server}{'!path'}.'/runserver.sh' or die 'Can not create launcher for server '.$Server.' : '.$!.$/;

open ZSERVCFG, '>'.$Servers{$Server}{'!path'}.'/zserv.cfg' or die 'Can not create zserv.cfg for server '.$Server.' : '.$!.$/;

print S_LAUNCHER $Launcher.$/;

close S_LAUNCHER;

my $CurServ_Message = 'INFO: Server '.$Server.' is going to run '.$Properties{'!mainwad'}.$/;

system ("echo ".$CurServ_Message);

# print 'INFO: Server launcher "'.$Servers{$Server}{'!path'}.'/runserver.sh" created.'.$/;

print ZSERVCFG $_.$/ foreach (@Z);

close ZSERVCFG;

print 'INFO: Configuration "'.$Servers{$Server}{'!path'}.'/zserv.cfg" created.'.$/;

# We need to update the rotation info.

push @New_Iteration, $Server.':'.$Servers{$Server}{'#Next'};

push @Server_Paths, $Servers{$Server}{'!path'};

}

# We need to update the !currentiteration value

$conf->setval("GENERAL", '!currentiteration', (join ',', @New_Iteration) );

$conf->RewriteConfig;

print 'INFO: '.$Config_File.' updated.'.$/;

# Creating a launcher for all rotating servers

my $Launcher_File = $conf->val("GENERAL", '!basepath').'/start_zservrota.sh';

$Launcher_File =~ s/\/+/\//g;

open LAUNCHER, '>'.$Launcher_File or die 'Can not create launcher : '.$!.$/;

print LAUNCHER 'killall -15 '.$conf->val("GENERAL", '!rotationexecutable').$/;

print LAUNCHER 'sleep 5'.$/;

my $Executable = $conf->val("GENERAL", '!rotationexecutable');

foreach (@Server_Paths) {

print LAUNCHER 'cd '.$_.$/;

print LAUNCHER 'test -e '.$Executable.' || cp zserv "'.$Executable.'"'.$/;

print LAUNCHER 'sh runserver.sh'.$/;

}

close LAUNCHER;

print 'INFO: Launcher "'.$Launcher_File.'" created.'.$/;

print '--- All done. ---'.$/;

#--- utility functions --------------------------------------------------------#

sub trim {

my $String = shift;

$String =~ s/^\s+//;

$String =~ s/\s+$//;

return $String;

}

sub unquote {

my $String = shift;

$String =~ s/^'/"/;

$String =~ s/'$/"/;

$String =~ s/^"|"$//g;

return $String;

}

sub wadfile_exists {

my $Wad_File = shift;

$Wad_File = trim($Wad_File);

$Wad_File .= '.wad' unless ($Wad_File =~ /\.wad$/i);

my @Parts = split /\//, $Wad_File;

my $Filename = pop @Parts;

my $Path = join '/', @Parts;

# We need only the filename without the last extension (should be 'wad')

my @Filename_Parts = split /\./, $Filename;

pop @Filename_Parts;

my $Wad_Name = join '.', @Filename_Parts;

my $Exists = 0;

# Since Unix is case sensitive, we need to check for all extensions

foreach ('WAD', 'wad', 'Wad', 'wAd', 'waD', 'WAd', 'wAD', 'WaD') {

if (-e $Path.'/'.$Wad_Name.'.'.$_) {

$Exists = 1;

last;

};

}

return ($Exists > 0) ? 1 : 0;

}