MinetestManagementConsole/mmc

901 lines
24 KiB
Perl
Executable File

#!/usr/bin/perl
use strict;
use warnings;
use UI::Dialog;
use Term::ReadKey;
use Term::ANSIScreen qw(cls);
use POSIX qw(strftime);
use Number::Bytes::Human qw(format_bytes parse_bytes);
my $FileEditor = "/bin/nano";
my $InitDName = "/home/mtowner/bin/minetest";
my $MINETESTDIR = "/home/mtowner/minetest";
my $MINEHOME = "/home/mtowner";
my $BackupCommand = "/home/mtowner/MineBackup/minebackup.pl";
my $UpdateCommand = "/usr/bin/git pull";
my $PagerCommand = "/usr/bin/less";
my $Logfile = "/home/mtowner/minetest/debug.txt";
my $PlayersDir = "/home/mtowner/minetest/worlds/world/players";
my $MMCDir = "/home/mtowner/MinetestManagementConsole";
my $BackupToolDir = "/home/mtowner/MineBackup";
my $StartupDir = "/home/mtowner/MinetestStartup";
my $MinetestConf = "/home/mtowner/minetest/minetest.conf";
my $MinetestWorld = "/home/mtowner/minetest/worlds/world/world.mt";
my $BACKUP_DIR = "/home/mtowner/backups";
my $RestoreCommand = "tar xvzf";
my $DEBUG_MODE = "off";
my $NewsFile = "/home/mtowner/minetest/worlds/world/news.txt";
my $BinDir = "/home/mtowner/bin";
my $ModDir = "/home/mtowner/minetest/mods";
my $KillAllString = "/home/mtowner/minetest/bin/minetestserver";
my $ServerProcess = "minetestserver";
my $ServerStartString = "/home/mtowner/minetest/bin/startminetest";
my $DfCommand = "df -h /dev/sda";
my $DuCommand = "du -h /home/mtowner/|tail -1";
my $FilesToRemove = "$MINETESTDIR/debug.txt $MINETESTDIR/MinetestScreen.log";
my $CrontabCommand = "crontab -e";
my $BuildCommand = "make";
my $WhoCommand = "/usr/bin/w";
my $LastCommand = "/usr/bin/last";
my $TopCommand = "/usr/bin/top";
###################################################
# No changes below here
###################################################
my $MySettings = "$ENV{'HOME'}/.mmcrc";
my $MMC_ver = "1.14.0";
my $Record = "false"; # Are results saved?
my $TempDir = "/tmp";
my $RobotName = "";
my $BotVersion = "";
my $UserName = "";
my $ServerStatus = "foo";
if ($ENV{'EDITOR'})
{
$FileEditor = $ENV{'EDITOR'};
}
my $menuselection = "";
sub PrintDebugCommand
{
if ($DEBUG_MODE eq "off")
{
return;
}
my $PassedString = shift;
print "About to run:\n$PassedString\n";
print "Press Enter To Run This:";
my $entered = <STDIN>;
}
my $d = new UI::Dialog ( backtitle => "Minetest Management Console v$MMC_ver", height => 20, width => 65, listheight => 5,
order => [ 'ascii', 'cdialog', 'xdialog' ]);
my $windowtitle = "Welcome to the Minetest Management Console!";
my $enjoyedtitle = "We hope you enjoyed MMC!";
my $introtext =
"This is the Minetest Management Console, a utility for Minetest operators
to manage their servers from a text GUI rather than the command line.";
$d->msgbox( title => $windowtitle, text => $introtext );
if (($d->state() eq "ESC") || ($d->state() eq "CANCEL"))
{
exit 0;
}
# Check for config file
if (-f $MySettings)
{
# Read in settings
open (my $FH, "<", $MySettings) or die "Could not read default file '$MySettings' $!";
while (<$FH>)
{
chop();
my ($Command, $Setting) = split(/=/, $_);
if ($Command eq "fileeditor")
{
$FileEditor = $Setting;
}
if ($Command eq "initdname")
{
$InitDName = $Setting;
}
if ($Command eq "minetestdir")
{
$MINETESTDIR = $Setting;
}
if ($Command eq "backupcommand")
{
$BackupCommand = $Setting;
}
if ($Command eq "updatecommand")
{
$UpdateCommand = $Setting;
}
if ($Command eq "pagercommand")
{
$PagerCommand = $Setting;
}
if ($Command eq "logfile")
{
$Logfile = $Setting;
}
if ($Command eq "playerdir")
{
$PlayersDir = $Setting;
}
if ($Command eq "mmcdir")
{
$MMCDir = $Setting;
}
if ($Command eq "startupdir")
{
$StartupDir = $Setting;
}
if ($Command eq "minetestconf")
{
$MinetestConf = $Setting;
}
if ($Command eq "minetestworld")
{
$MinetestWorld = $Setting;
}
if ($Command eq "newsfile")
{
$NewsFile = $Setting;
}
if ($Command eq "debugmode")
{
$DEBUG_MODE = $Setting;
}
if ($Command eq "backupdir")
{
$BACKUP_DIR = $Setting;
}
if ($Command eq "bindir")
{
$BinDir = $Setting;
}
if ($Command eq "backuptooldir")
{
$BackupToolDir = $Setting;
}
if ($Command eq "restorecommand")
{
$RestoreCommand = $Setting;
}
if ($Command eq "minehome")
{
$MINEHOME = $Setting;
}
}
close($FH);
}
else
{
# Store defaults
open (my $FH, ">", $MySettings) or die "Could not create default file '$MySettings' $!";
print $FH "fileeditor=$FileEditor\n";
print $FH "initdname=$InitDName\n";
print $FH "minetestdir=$MINETESTDIR\n";
print $FH "backupcommand=$BackupCommand\n";
print $FH "updatecommand=$UpdateCommand\n";
print $FH "pagercommand=$PagerCommand\n";
print $FH "logfile=$Logfile\n";
print $FH "playerdir=$PlayersDir\n";
print $FH "mmcdir=$MMCDir\n";
print $FH "startupdir=$StartupDir\n";
print $FH "minetestconf=$MINETESTDIR/minetest.conf\n";
print $FH "minetestworld=$MINETESTDIR/worlds/world/world.mt\n";
print $FH "newsfile=$MINETESTDIR/worlds/world/news.txt\n";
print $FH "debugmode=$DEBUG_MODE\n";
print $FH "backupdir=$BACKUP_DIR\n";
print $FH "bindir=$BinDir\n";
print $FH "backuptooldir=$BackupToolDir\n";
print $FH "restorecommand=$RestoreCommand\n";
print $FH "minehome=$MINEHOME\n";
close($FH);
}
sub CheckServerStatus
{
my $running=`ps ax|grep $ServerProcess|grep -v grep`;
if ($running ne "")
{
$ServerStatus = "Running";
}
else
{
$ServerStatus = "Stopped";
}
}
sub BackupToolsMenu
{
my $WantRespawn="ON";
CheckServerStatus();
if (-f "$MINETESTDIR/nostart")
{
$WantRespawn="OFF";
}
$menuselection = $d->menu( title => "Backup Tools Menu", text => "Server is $ServerStatus and respawn is $WantRespawn - Select one:",
list => [ '1', 'Run Backup',
'2', 'Restore Backup',
'q', 'Main Menu' ] );
}
sub UpdateToolsMenu
{
my $WantRespawn="ON";
CheckServerStatus();
if (-f "$MINETESTDIR/nostart")
{
$WantRespawn="OFF";
}
$menuselection = $d->menu( title => "Update Tools Menu", text => "Server is $ServerStatus and respawn is $WantRespawn - Select one:",
list => [ '1', 'Update MMC',
'2', 'Update Backup',
'3', 'Update Startup',
'q', 'Main Menu' ] );
}
sub GetWhichBackup
{
my $UsedFile = 0;
opendir(DIR, $BACKUP_DIR) or die $!;
my @file_list;
while ( my $file = readdir(DIR))
{
# We only want files
next unless (-f "$BACKUP_DIR/$file");
# Use a regular expression to find files ending in .tgz
next unless ($file =~ m/\.tgz$/);
push @file_list, "$BACKUP_DIR/$file";
}
closedir(DIR);
#print scalar @file_list;
system("clear");
print "Backup\tBackup Date\t\tSize\n";
for my $file (sort
{
my @a_stat = stat($a);
my @b_stat = stat($b);
$b_stat[8] <=> $a_stat[8];
} @file_list ) {
$UsedFile++;
my @stat = stat ("$file");
my $size = format_bytes($stat[7], bs => 1000);
print "$UsedFile\t" . strftime("%m/%d/%Y %H:%M:%S", localtime($stat[8])) . "\t$size ($file)\n";
};
#Ask user which one
while(-1)
{
print "Restore which backup? (1-5) or \"quit\": ";
my $Chosen = <STDIN>;
chop ($Chosen);
if ((lc($Chosen) eq "q") || (lc($Chosen) eq "quit") || ($Chosen eq ""))
{
return "";
}
my $IntChosen = int($Chosen);
if (($IntChosen >= 1) && ($IntChosen <= 5))
{
return $Chosen;
}
}
}
sub DoRestore
{
my $WhichBackup = "";
# Get which backup they want
$WhichBackup = GetWhichBackup();
if ($WhichBackup eq "")
{
print "No backup selected\n";
return;
}
print "Using backup $WhichBackup...\n";
# Turn off respawn
PrintDebugCommand("touch $MINETESTDIR/nostart");
system("touch $MINETESTDIR/nostart");
# Kill the server process
print "Killing Minetest process, please wait...\n";
system("killall $ServerStartString");
sleep(5);
# Create new folder
print "Creating new folder, please wait...";
PrintDebugCommand("mkdir $MINEHOME-new");
system("mkdir $MINEHOME-new");
PrintDebugCommand("touch $MINEHOME-new/nostart");
system("touch $MINEHOME-new/nostart");
# Swap folders
print "Swapping folders, please wait...";
PrintDebugCommand("mv $MINEHOME/minetest $MINEHOME/minetest-keep");
system("mv $MINEHOME/minetest $MINEHOME/minetest-keep");
PrintDebugCommand("mv $MINEHOME/minetest-new $MINEHOME/minetest");
system("mv $MINEHOME-new $MINETESTDIR");
PrintDebugCommand("rm -r $MINEHOME/minetest-keep");
system("rm -r $MINEHOME/minetest-keep");
# Do the restore
chdir ($MINEHOME);
PrintDebugCommand("$RestoreCommand $BACKUP_DIR/minebackup-$WhichBackup.tgz");
system("$RestoreCommand $BACKUP_DIR/minebackup-$WhichBackup.tgz");
unlink("$MINETESTDIR/nostart");
}
sub BackupTools
{
while (-1)
{
BackupToolsMenu();
if (($menuselection eq "CANCEL") || ($menuselection eq "ESC") || ($menuselection eq "") || ($menuselection eq "q") || ($menuselection eq "Q"))
{
return;
}
elsif ($menuselection eq "1")
{
# Run a backup
PrintDebugCommand("$BackupCommand");
system("$BackupCommand");
print "--- Press Enter To Continue ---";
my $entered = <STDIN>;
}
elsif ($menuselection eq "2")
{
# Do a restore
DoRestore();
print "--- Press Enter To Continue ---";
my $entered = <STDIN>;
}
}
}
sub RebuildServer
{
chdir($MINETESTDIR);
system("clear");
system($BuildCommand);
print "--- Press Enter To Continue ---";
my $entered = <STDIN>;
}
sub UpdateServer
{
chdir($MINETESTDIR);
system("clear");
system($UpdateCommand);
print "--- Press Enter To Continue ---";
my $entered = <STDIN>;
}
sub UpdateMods
{
chdir($ModDir);
system("clear");
print "Updating your mods\n";
print "==================\n";
my $ModPrompt = "";
my $entered = "";
# system($UpdateCommand);
opendir(DIR, $ModDir) or die "Could not open $ModDir\n";
# Loop for each directory
while (my $filename = readdir(DIR))
{
if (($filename eq ".") || ($filename eq ".."))
{
# Skip over "." and ".."
next;
}
#print "$filename\n" if -d $filename;
if (-d $filename)
{
print "Current mod: $filename:\n";
print "Update this mod (yes/no/quit):";
$ModPrompt = <STDIN>;
chop($ModPrompt);
if (($ModPrompt eq "quit") || ($ModPrompt eq "q"))
{
last;
}
if (($ModPrompt eq "no") || ($ModPrompt eq "n"))
{
next;
}
# chdir("$ModDir/$filename");
system("git pull");
# print "--- Press Enter To Continue ---";
# my $entered = <STDIN>;
}
}
closedir(DIR);
print "--- Press Enter To Continue ---";
$entered = <STDIN>;
}
sub UpdateGame
{
chdir("$MINETESTDIR/game/minetest_game");
system("clear");
system($UpdateCommand);
print "--- Press Enter To Continue ---";
my $entered = <STDIN>;
}
sub ServerToolsMenu
{
my $WantRespawn="ON";
CheckServerStatus();
if (-f "$MINETESTDIR/nostart")
{
$WantRespawn="OFF";
}
$menuselection = $d->menu( title => "Server Tools Menu", text => "Server is $ServerStatus and respawn is $WantRespawn - Select one:",
list => [ '1', 'Run Backup First',
'2', 'Get Updates',
'3', 'Rebuild Server',
'4', 'Update Game',
'5', 'Update Mods',
'q', 'Main Menu' ] );
}
sub ServerTools
{
while (-1)
{
ServerToolsMenu();
if (($menuselection eq "CANCEL") || ($menuselection eq "ESC") || ($menuselection eq "") || ($menuselection eq "q") || ($menuselection eq "Q"))
{
return;
}
elsif ($menuselection eq "1")
{
# Run a backup
system("clear");
PrintDebugCommand("$BackupCommand");
system("$BackupCommand");
print "--- Press Enter To Continue ---";
my $entered = <STDIN>;
}
elsif ($menuselection eq "2")
{
# Pull Updates
PrintDebugCommand("$UpdateCommand");
UpdateServer();
}
elsif ($menuselection eq "3")
{
# Rebuild Server
PrintDebugCommand("$BuildCommand");
RebuildServer();
}
elsif ($menuselection eq "4")
{
# Rebuild Server
PrintDebugCommand("Updating Game");
UpdateGame();
}
elsif ($menuselection eq "5")
{
# Rebuild Server
PrintDebugCommand("Updating Mods");
UpdateMods();
}
}
}
sub ServerStatusMenu
{
my $WantRespawn="ON";
CheckServerStatus();
if (-f "$MINETESTDIR/nostart")
{
$WantRespawn="OFF";
}
$menuselection = $d->menu( title => "Server Status Menu", text => "Server is $ServerStatus and respawn is $WantRespawn - Select one:",
list => [ '1', 'Free Space',
'2', 'Current Version',
'3', 'Shell Users',
'4', 'Last Shell Logins',
'5', 'Running Processes',
'q', 'Main Menu' ] );
}
sub ServerStatusTools
{
while (-1)
{
ServerStatusMenu();
if (($menuselection eq "CANCEL") || ($menuselection eq "ESC") || ($menuselection eq "") || ($menuselection eq "q") || ($menuselection eq "Q"))
{
return;
}
elsif ($menuselection eq "1")
{
# Space Usage
system("clear");
SpaceUsage();
print "--- Press Enter To Continue ---";
my $entered = <STDIN>;
}
elsif ($menuselection eq "2")
{
# Get Version
system("clear");
system("$MINETESTDIR/bin/$ServerProcess --version");
print "--- Press Enter To Continue ---";
my $entered = <STDIN>;
}
elsif ($menuselection eq "3")
{
# Who's Online
system("clear");
system("$WhoCommand");
print "--- Press Enter To Continue ---";
my $entered = <STDIN>;
}
elsif ($menuselection eq "4")
{
# Last Logins
system("clear");
system("$LastCommand");
print "--- Press Enter To Continue ---";
my $entered = <STDIN>;
}
elsif ($menuselection eq "5")
{
# Top Processes
system("clear");
print "--- To exit this command press \"q\" ---";
my $entered = <STDIN>;
system("$TopCommand");
}
}
}
sub DoUpdate
{
my $DesiredDir = $_[0];
# Update desired utility
chdir ($DesiredDir);
PrintDebugCommand("Running $UpdateCommand in $DesiredDir\n");
system("$UpdateCommand");
print "--- Press Enter To Continue ---";
my $entered = <STDIN>;
}
sub UpdateTools
{
while (-1)
{
UpdateToolsMenu();
if (($menuselection eq "CANCEL") || ($menuselection eq "ESC") || ($menuselection eq "") || ($menuselection eq "q") || ($menuselection eq "Q"))
{
return;
}
elsif ($menuselection eq "1")
{
# Update MMC
DoUpdate($MMCDir);
}
elsif ($menuselection eq "2")
{
# Update BackupToolDir
DoUpdate($BackupToolDir);
}
elsif ($menuselection eq "3")
{
# Update Startup
DoUpdate($StartupDir);
system("cp $StartupDir/minetest $BinDir");
system("cp $StartupDir/startrelay $BinDir");
system("cp $StartupDir/startminetest $MINETESTDIR");
}
}
}
sub InstallMod
{
system("clear");
print "Enter the URL for the mod: ";
my $modurl = <>;
chomp($modurl);
if ($modurl eq "")
{
# Input was blank
print "No URL Specified - aborting\n";
sleep 5;
return;
}
# Try and validate URL
if( lc(substr($modurl, 0, 4)) ne "http")
{
# Doesn;t start with http
print "URL doesn't start with http - aborting\n";
sleep 5;
return;
}
if( lc(substr($modurl, -4)) ne ".git")
{
# Doesn;t end in .git
print "URL doesn't end with .git - aborting\n";
sleep 5;
return;
}
print "URL looks good - trying to clone\n";
chdir($ModDir);
system("git clone $modurl");
print "Were any errors reported? (N/y):";
my $input = <>;
chomp($input);
if (lc($input) eq "y")
{
print "Clone failed, please check URL or correct the issue and try again\n";
sleep 5;
return;
}
system("echo \"-----\n\" >> $MinetestWorld");
if ($? != 0)
{
print "Could not append to file '$MinetestWorld' - check perms: $!";
sleep 5;
return;
}
print "Killing Server Process\n";
system("killall $KillAllString");
print "Process killed...";
print "Waiting for server to restart\n";
my $count = 0;
while ($count < 120)
{
$count += 5;
print "$count ";
sleep 5;
}
print "Checking for new mod in world.mt\n";
my $file = $MinetestWorld;
my $SeenStart = 0;
open(FH, $file) or die("Unable to open file $file for reading");
open(FH2, ">/tmp/newfile") or die("Unable to open file $file for writing");
while(my $String = <FH>)
{
if ($_)
{
chop();
}
if($String =~ /-----/)
{
$SeenStart = -1;
print "Saw Marker\n";
next;
}
if ($SeenStart)
{
my $NewString = $String =~ s/= false/= true/r;
print(FH2 $NewString);
}
else
{
print(FH2 $String);
}
}
close(FH);
close(FH2);
system("cp /tmp/newfile $file");
print "Killing Server Process\n";
system("killall $KillAllString");
print "Process killed...";
print "Waiting for server to restart\n";
my $count = 0;
while ($count < 120)
{
$count += 5;
print "$count ";
sleep 5;
}
print "\nInstall prooess completed\n";
sleep 5;
}
sub RemoveFiles
{
for (split ' ', $FilesToRemove)
{
if (-f $_)
{
unlink($_);
}
}
}
sub SpaceUsage
{
system("clear");
print "System Space:\n";
print "==================\n";
system("$DfCommand");
print "\nSpace Used by You:\n";
print "==================\n";
system("$DuCommand");
print "\nDo you want to remove logs and other expendable items? (N/y): ";
my $input = <>;
chomp($input);
$input = lc($input);
if (($input eq "y") || ($input eq "yes"))
{
RemoveFiles();
print "Possible files removed...\n";
sleep 5;
return;
}
print "Leaving temporary files alone\n";
}
sub MainMenu
{
my $WantRespawn="ON";
CheckServerStatus();
if (-f "$MINETESTDIR/nostart")
{
$WantRespawn="OFF";
}
$menuselection = $d->menu( title => "Main Menu", text => "Server is $ServerStatus and respawn is $WantRespawn - Select one:",
list => [ '1', 'Start Watchdog',
'2', 'Stop Watchdog',
'3', 'Server Console',
'4', 'Turn Off Respawn',
'5', 'Turn Respawn On',
'6', 'Edit World Cfg',
'7', 'Edit minetest.conf',
'8', 'Backup / Restore',
'9', 'View Log',
'10', 'Online Players',
'11', 'Edit Settings',
'12', 'Restart Server',
'13', 'Edit News.txt',
'14', 'Update Utils',
'15', 'Install Mod',
'16', 'Server Status',
'17', 'Scheduled Tasks',
'18', 'Server Tasks',
'q', 'Quit MMC' ] );
}
while (-1)
{
MainMenu();
if (($menuselection eq "CANCEL") || ($menuselection eq "ESC") || ($menuselection eq "") || ($menuselection eq "q") || ($menuselection eq "Q"))
{
$d->msgbox( title => $enjoyedtitle, text => "Thanks for using MMC..." );
exit 0;
}
if ($menuselection eq "1")
{
if ($d->yesno( text => "Confirm starting the server", text => "Are you sure you want to start the server? You should likely use option #12 instead..." ))
{
PrintDebugCommand("$InitDName start");
system("$InitDName start");
sleep(10);
}
}
elsif ($menuselection eq "2")
{
if ($d->yesno( text => "Confirm stopping the server", text => "Are you sure you want to shut down the server?" ))
{
system("$InitDName stop");
sleep(5);
system("killall $ServerStartString");
}
}
elsif ($menuselection eq "3")
{
$d->msgbox( text => "To exit the minetest console and return to MMC press CTRL-A CTRL-D" );
system("screen -r Minetest");
}
elsif ($menuselection eq "4")
{
# Turn off respawn
system("touch $MINETESTDIR/nostart");
}
elsif ($menuselection eq "5")
{
# Turn respawn back on
if (-f "$MINETESTDIR/nostart")
{
unlink("$MINETESTDIR/nostart");
}
}
elsif ($menuselection eq "6")
{
# Edit world.mt file
system("$FileEditor $MinetestWorld");
}
elsif ($menuselection eq "7")
{
# Edit minetest.conf file
system("$FileEditor $MinetestConf");
}
elsif ($menuselection eq "8")
{
# Backup Menu
BackupTools();
}
elsif ($menuselection eq "9")
{
# View log file
system("$PagerCommand $Logfile");
}
elsif ($menuselection eq "10")
{
# View online players
system("clear");
system("ls $PlayersDir");
print "--- Press Enter To Continue ---";
my $entered = <STDIN>;
}
elsif ($menuselection eq "11")
{
# Edit minetest.conf file
system("$FileEditor $MySettings");
}
elsif ($menuselection eq "12")
{
# Reset server process
print "Killing Server Process\n";
system("killall $KillAllString");
print "Process killed...";
sleep(5);
}
elsif ($menuselection eq "13")
{
# Edit news.txt file
system("$FileEditor $NewsFile");
}
elsif ($menuselection eq "14")
{
# Update Menu
UpdateTools();
}
elsif ($menuselection eq "15")
{
# Install Mod
InstallMod();
}
elsif ($menuselection eq "16")
{
# Server Status Tools
ServerStatusTools();
}
elsif ($menuselection eq "17")
{
# Space Usage
system("$CrontabCommand");
}
elsif ($menuselection eq "18")
{
# Server Tools
ServerTools();
}
}
exit 0;