#!/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 = ; } 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 = ; 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 = ; } elsif ($menuselection eq "2") { # Do a restore DoRestore(); print "--- Press Enter To Continue ---"; my $entered = ; } } } sub RebuildServer { chdir($MINETESTDIR); system("clear"); system($BuildCommand); print "--- Press Enter To Continue ---"; my $entered = ; } sub UpdateServer { chdir($MINETESTDIR); system("clear"); system($UpdateCommand); print "--- Press Enter To Continue ---"; my $entered = ; } 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 = ; 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 = ; } } closedir(DIR); print "--- Press Enter To Continue ---"; $entered = ; } sub UpdateGame { chdir("$MINETESTDIR/game/minetest_game"); system("clear"); system($UpdateCommand); print "--- Press Enter To Continue ---"; my $entered = ; } 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 = ; } 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 = ; } elsif ($menuselection eq "2") { # Get Version system("clear"); system("$MINETESTDIR/bin/$ServerProcess --version"); print "--- Press Enter To Continue ---"; my $entered = ; } elsif ($menuselection eq "3") { # Who's Online system("clear"); system("$WhoCommand"); print "--- Press Enter To Continue ---"; my $entered = ; } elsif ($menuselection eq "4") { # Last Logins system("clear"); system("$LastCommand"); print "--- Press Enter To Continue ---"; my $entered = ; } elsif ($menuselection eq "5") { # Top Processes system("clear"); print "--- To exit this command press \"q\" ---"; my $entered = ; 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 = ; } 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 = ) { 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 = ; } 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;