From 268fb58bb95a120b1b67c00eb4756bf630a8ca76 Mon Sep 17 00:00:00 2001 From: Jamie Cameron Date: Sun, 10 Mar 2013 21:59:31 -0700 Subject: [PATCH] Completed minecraft backup feature --- minecraft/CHANGELOG | 5 ++ minecraft/images/backup.gif | Bin 0 -> 3752 bytes minecraft/index.cgi | 11 ++-- minecraft/lang/en | 19 ++++++ minecraft/log_parser.pl | 7 ++- minecraft/minecraft-lib.pl | 113 ++++++++++++++++++++++++++++++++++++ minecraft/module.info | 2 +- minecraft/save_world.cgi | 2 +- minecraft/view_conn.cgi | 2 +- 9 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 minecraft/CHANGELOG create mode 100644 minecraft/images/backup.gif 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 0000000000000000000000000000000000000000..ca711648f11e180c7d7fcd355f05f982797654f9 GIT binary patch literal 3752 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 literal 0 HcmV?d00001 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'} ".