diff --git a/minecraft/CHANGELOG b/minecraft/CHANGELOG new file mode 100644 index 000000000..514a0d0e1 --- /dev/null +++ b/minecraft/CHANGELOG @@ -0,0 +1,5 @@ +---- Changes since 1.0 ---- +Added a page for setting up scheduled backups of some or all worlds on the server. +Added a warning message on the main page if the server version is out of date. +German translations from Raymond Vetter. +Added a list of recent events for a player to the Manage Player page. diff --git a/minecraft/images/backup.gif b/minecraft/images/backup.gif new file mode 100644 index 000000000..ca711648f Binary files /dev/null and b/minecraft/images/backup.gif differ diff --git a/minecraft/index.cgi b/minecraft/index.cgi index 729723c7e..492327e49 100755 --- a/minecraft/index.cgi +++ b/minecraft/index.cgi @@ -61,16 +61,19 @@ if ($config{'last_size'}) { my @links = ( "edit_conf.cgi", "edit_users.cgi", "view_logs.cgi", "list_conns.cgi", "list_worlds.cgi", "edit_cmds.cgi", - "console.cgi", "edit_manual.cgi" ); + "console.cgi", "edit_backup.cgi", + "edit_manual.cgi" ); my @titles = ( $text{'conf_title'}, $text{'users_title'}, $text{'logs_title'}, $text{'conns_title'}, $text{'worlds_title'}, $text{'cmds_title'}, - $text{'console_title'}, $text{'manual_title'} ); + $text{'console_title'}, $text{'backup_title'}, + $text{'manual_title'} ); my @icons = ( "images/conf.gif", "images/users.gif", "images/logs.gif", "images/conns.gif", "images/worlds.gif", "images/cmds.gif", - "images/console.gif", "images/manual.gif" ); -&icons_table(\@links, \@titles, \@icons); + "images/console.gif", "images/backup.gif", + "images/manual.gif" ); +&icons_table(\@links, \@titles, \@icons, 5); print &ui_hr(); print &ui_buttons_start(); diff --git a/minecraft/lang/en b/minecraft/lang/en index db987bd5f..157a5ae77 100644 --- a/minecraft/lang/en +++ b/minecraft/lang/en @@ -257,6 +257,23 @@ download_err=Failed to setup Minecraft server download_edir=Missing or non-absolute install directory download_euser=User to run as does not exist +backup_title=Scheduled Backup +backup_header=Options for scheduled world backup +backup_sched=Backup schedule +backup_enabled=Backup enabled? +backup_dir=Backup to directory +backup_worlds=Worlds to include +backup_worlds1=All worlds +backup_worlds0=Only selected .. +backup_err=Failed to save scheduled backup +backup_edir=Missing or non-absolute destination directory +backup_eworlds=No worlds selected +backup_desc=This page allows you to setup automatic scheduled backups of your Minecraft server worlds. The destination directory can contain strftime-style date codes like %d, %m and %Y to save separate daily backups. +backup_email=Email backup report to +backup_noemail=Nobody +backup_email_err=Only send email on failure +backup_emailto=Address + log_conf=Changed server configuration log_stop=Stopped Minecraft server log_start=Started Minecraft server @@ -267,3 +284,5 @@ log_ip=Updated banned IPs log_manual=Manually edited configuration file log_atboot=Enabled server at boot time log_delboot=Disabled server at boot time +log_enable_backup=Enabled scheduled backups to $1 +log_disable_backup=Disabled scheduled backups diff --git a/minecraft/log_parser.pl b/minecraft/log_parser.pl index c9daf6197..6569c55e5 100755 --- a/minecraft/log_parser.pl +++ b/minecraft/log_parser.pl @@ -8,6 +8,11 @@ do 'minecraft-lib.pl'; sub parse_webmin_log { my ($user, $script, $action, $type, $object, $p) = @_; -return $text{'log_'.$action}; +if ($object eq 'backup') { + return &text('log_'.$action.'_backup', $object); + } +else { + return $text{'log_'.$action}; + } } diff --git a/minecraft/minecraft-lib.pl b/minecraft/minecraft-lib.pl index dff206321..f2691f467 100644 --- a/minecraft/minecraft-lib.pl +++ b/minecraft/minecraft-lib.pl @@ -5,6 +5,7 @@ use strict; use warnings; use WebminCore; use Time::Local; +use POSIX; &init_config(); our ($module_root_directory, %text, %gconfig, $root_directory, %config, $module_name, $remote_user, $base_remote_user, $gpgpath, @@ -199,6 +200,7 @@ my $pid = &is_minecraft_server_running(); $pid || return "Not running!"; # Try graceful shutdown +&send_server_command("/save-all"); &send_server_command("/stop"); for(my $i=0; $i<10; $i++) { last if (!&is_minecraft_server_running()); @@ -605,4 +607,115 @@ while(1) { return $header{'content-length'}; } +# get_backup_job() +# Returns the webmincron job to backup worlds +sub get_backup_job +{ +&foreign_require("webmincron"); +my @jobs = &webmincron::list_webmin_crons(); +my ($job) = grep { $_->{'module'} eq $module_name && + $_->{'func'} eq "backup_worlds" } @jobs; +return $job; +} + +# backup_worlds() +# This function is called by webmincron to perform a backup +sub backup_worlds +{ +# Get worlds to include +my @allworlds = &list_worlds(); +my @worlds; +if ($config{'backup_worlds'}) { + my %names = map { $_, 1 } split(/\s+/, $config{'backup_worlds'}); + @worlds = grep { $names{$_->{'name'}} } @allworlds; + } +else { + @worlds = @allworlds; + } +if (!@worlds) { + &send_backup_email("No worlds were found to backup!", 1); + return; + } + +# Get destination dir, with strftime +my @tm = localtime(time()); +&clear_time_locale(); +my $dir = strftime($config{'backup_dir'}, @tm); +&reset_time_locale(); + +# Create destination dir +if (!-d $dir) { + if (!&make_dir($dir, 0700)) { + &send_backup_email( + "Failed to create destination directory $dir : $!"); + return; + } + if ($config{'unix_user'} ne 'root') { + &set_ownership_permissions($config{'unix_user'}, undef, undef, + $dir); + } + } + +# Find active world +my $conf = &get_minecraft_config(); +my $def = &find_value("level-name", $conf); + +# Backup each world +my @out; +my $failed = 0; +foreach my $w (@worlds) { + my $file = "$dir/$w->{'name'}.zip"; + push(@out, "Backing up $w->{'name'} to $file .."); + if ($w->{'name'} eq $def && + &is_minecraft_server_running()) { + # World is live, flush state to disk + &execute_minecraft_command("save-off"); + &execute_minecraft_command("save-all"); + } + my $out = &backquote_command( + "cd ".quotemeta($config{'minecraft_dir'})." && ". + "zip -r ".quotemeta($file)." ".quotemeta($w->{'name'})); + my $ex = $?; + if ($w->{'name'} eq $def && + &is_minecraft_server_running()) { + # Re-enable world writes + &execute_minecraft_command("save-on"); + } + my @st = stat($file); + if (@st && $config{'unix_user'} ne 'root') { + &set_ownership_permissions($config{'unix_user'}, undef, undef, + $file); + } + if ($ex) { + push(@out, " .. ZIP of $w->{'name'} failed : $out"); + $failed++; + } + elsif (!@st) { + push(@out, " .. ZIP of $w->{'name'} produced no output : $out"); + $failed++; + } + else { + push(@out, " .. done (".&nice_size($st[7]).")"); + } + push(@out, ""); + } +&send_backup_email(join("\n", @out)."\n", $failed); +} + +# send_backup_email(msg, error) +# Sends a backup report email, if configured +sub send_backup_email +{ +my ($msg, $err) = @_; +return 0 if (!$config{'backup_email'}); +return 0 if ($config{'backup_email_err'} && !$err); +&foreign_require("mailboxes"); +&mailboxes::send_text_mail( + &mailboxes::get_from_address(), + $config{'backup_email'}, + undef, + "Minecraft backup ".($err ? "FAILED" : "succeeded"), + $msg); +} + 1; diff --git a/minecraft/module.info b/minecraft/module.info index 1030af482..96ff811f6 100644 --- a/minecraft/module.info +++ b/minecraft/module.info @@ -2,4 +2,4 @@ desc=Minecraft Server depends=init proc category=servers desc_de=Minecraft Server -version=1.0 +version=1.1 diff --git a/minecraft/save_world.cgi b/minecraft/save_world.cgi index 15f061f9d..f9cc1cc10 100755 --- a/minecraft/save_world.cgi +++ b/minecraft/save_world.cgi @@ -112,8 +112,8 @@ elsif ($in{'download'} && $ENV{'PATH_INFO'}) { if (&is_minecraft_server_running() && $def eq $in{'name'}) { # Flush state to disk - &execute_minecraft_command("save-all"); &execute_minecraft_command("save-off"); + &execute_minecraft_command("save-all"); } my $temp = &transname().".zip"; my $out = &backquote_command( diff --git a/minecraft/view_conn.cgi b/minecraft/view_conn.cgi index f39ece4f1..b5b79e8b2 100755 --- a/minecraft/view_conn.cgi +++ b/minecraft/view_conn.cgi @@ -93,7 +93,7 @@ if ($c || 1) { # Ban or un-ban player my @banlist = &list_banned_players(); - my ($b) = grep { $_ eq $in{'name'} } @banlist; + my ($b) = grep { lc($_) eq lc($in{'name'}) } @banlist; if ($b) { print &ui_table_row($text{'conn_banlist'}, "$text{'conn_banned'} ".