224 lines
7.7 KiB
Awk
224 lines
7.7 KiB
Awk
|
#!/bin/awk -f
|
||
|
|
||
|
################################################################################
|
||
|
# Database Import Script for Auth Redux Mod (Step 2)
|
||
|
# ---------------------------------------------------
|
||
|
# WARNING: This script is to be run immediately after the database conversion.
|
||
|
#
|
||
|
# This script will extract player login activity from the specified 'debug.txt'
|
||
|
# file and produce an import journal in the world directory. For best results,
|
||
|
# the 'auth.txt' and 'debug.txt' files should be complete and current. If they
|
||
|
# are not entirely synchronous, then errors are likely to result.
|
||
|
#
|
||
|
# Lastly, run the rollback.lua script from the command line. This will replay
|
||
|
# the import journal against the newly converted database, applying all player
|
||
|
# login activity as it was derived from the corresponding debug log.
|
||
|
#
|
||
|
# Below is the required sequence of commands:
|
||
|
#
|
||
|
# > awk -f convert.awk -v mode=convert ~/.minetest/worlds/world/auth.txt
|
||
|
# > awk -f extract.awk ~/.minetest/worlds/world/auth.txt ~/.minetest/debug.txt
|
||
|
# > lua rollback.lua ~/.minetest/worlds/world/auth.db
|
||
|
#
|
||
|
# It is not necessary to operate on the original files. They can be moved into
|
||
|
# into a temporary subdirectory for use with all of these scripts.
|
||
|
#
|
||
|
# WARNING: The 'auth.dbx' file will be overwritten. This should not cause any
|
||
|
# problems if you began with a freshly converted database. When in doubt, then
|
||
|
# you may want to backup the 'auth.db' and 'auth.dbx' files as a precaution.
|
||
|
#
|
||
|
# For more detailed output, you can change the debug level as follows:
|
||
|
#
|
||
|
# > awk -f extract.awk -v debug=verbose ...
|
||
|
#
|
||
|
# This will display both errors and warnings. It might be helpful to redirect
|
||
|
# output to a temporary file for this purpose. To see only errors, the default
|
||
|
# debug level of 'terse' should be sufficient.
|
||
|
#
|
||
|
# Warnings occur if there are orphaned accounts in the 'auth.txt' file that do
|
||
|
# not appear in the corresponding debug log. This happens when the debug log
|
||
|
# is incomplete. Orphaned accounts will have no player login activity applied.
|
||
|
#
|
||
|
# Errors occur if the 'auth.txt' file is inconsistent with the debug log. This
|
||
|
# could be the result of a server crash or intentional deletion of accounts.
|
||
|
# Such accounts are deemed invalid and player login activity will be ignored.
|
||
|
# However, you should verify that you are using the correct 'auth.txt' file.
|
||
|
################################################################################
|
||
|
|
||
|
# USAGE EXAMPLE:
|
||
|
# awk -f extract.awk -v debug=terse /home/minetest/.minetest/worlds/world/auth.txt /home/minetest/.minetest/debug.txt
|
||
|
|
||
|
function get_timestamp( date_str, time_str ) {
|
||
|
return mktime( sprintf( "%d %d %d %d %d %d", \
|
||
|
substr( date_str, 1, 4 ), substr( date_str, 6, 2 ), substr( date_str, 9, 2 ), substr( time_str, 1, 2 ), substr( time_str, 4, 2 ), substr( time_str, 7, 2 ) ) );
|
||
|
}
|
||
|
|
||
|
function print_info( source, result ) {
|
||
|
if( is_verbose == 1 ) {
|
||
|
print "[" source "]", result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function check_user( name ) {
|
||
|
if( name in db_users ) {
|
||
|
++db_users[ name ];
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if( !log_users[ name ] ) {
|
||
|
print "ERROR: Player '" name "' does not exist in auth.txt file.";
|
||
|
}
|
||
|
return ++log_users[ name ];
|
||
|
}
|
||
|
|
||
|
function trim( str ) {
|
||
|
return substr( str, 2, length( str ) - 2 )
|
||
|
}
|
||
|
|
||
|
BEGIN {
|
||
|
FS = " "
|
||
|
|
||
|
LOG_STARTED = 10
|
||
|
LOG_STOPPED = 12
|
||
|
LOG_TOUCHED = 13
|
||
|
TX_SESSION_OPENED = 50
|
||
|
TX_SESSION_CLOSED = 51
|
||
|
TX_LOGIN_FAILURE = 31
|
||
|
TX_LOGIN_SUCCESS = 32
|
||
|
|
||
|
journal_file = "auth.dbx";
|
||
|
is_started = 0;
|
||
|
is_verbose = 0;
|
||
|
|
||
|
total_records = 0;
|
||
|
|
||
|
if( debug == "verbose" ) {
|
||
|
is_verbose = 1;
|
||
|
}
|
||
|
else if( debug != "terse" ) {
|
||
|
print "ERROR: Unknown argument, defaulting to terse debug level.";
|
||
|
}
|
||
|
|
||
|
if( ARGC != 3 ) {
|
||
|
print( "The required arguments are missing, aborting." )
|
||
|
exit 1;
|
||
|
}
|
||
|
|
||
|
world_path = ARGV[ 1 ];
|
||
|
if( sub( /auth.txt$/, "", world_path ) == 0 ) {
|
||
|
print( "The specified auth.txt file is not recognized, aborting." )
|
||
|
exit 1;
|
||
|
}
|
||
|
|
||
|
if( ARGV[ 2 ] != "debug.txt" && ARGV[ 2 ] !~ /\/debug.txt$/ ) {
|
||
|
print( "The specified debug.txt file is not recognized, aborting." )
|
||
|
exit 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# parse the 'auth.txt' file
|
||
|
|
||
|
ARGIND == 1 && FNR == 1 {
|
||
|
print( "Reading the " world_path "auth.txt file..." )
|
||
|
}
|
||
|
|
||
|
ARGIND == 1 {
|
||
|
name = substr( $0, 1, index( $0, ":" ) - 1 )
|
||
|
db_users[ name ] = 0;
|
||
|
total_records++;
|
||
|
}
|
||
|
|
||
|
# parse the 'debug.txt' file
|
||
|
|
||
|
ARGIND == 2 && FNR == 1 {
|
||
|
print( "Reading the " world_path "debug.txt file..." )
|
||
|
}
|
||
|
|
||
|
ARGIND == 2 {
|
||
|
cur_date_str = $1;
|
||
|
cur_time_str = $2;
|
||
|
|
||
|
if( $3 == "ACTION[Main]:" && ( $4 FS $5 ) == "World at" ) {
|
||
|
|
||
|
# 2018-07-10 12:16:09: ACTION[Main]: World at [/root/.minetest/worlds/world]
|
||
|
print_info( "debug.txt", $1 " @connect" );
|
||
|
|
||
|
print get_timestamp( cur_date_str, cur_time_str ), LOG_STARTED > ( world_path journal_file );
|
||
|
is_started = 1;
|
||
|
}
|
||
|
else if( !is_started ) {
|
||
|
# sanity check since mod loading errors precede startup logging
|
||
|
# and shutdowns are not always immediate after they are logged.
|
||
|
next;
|
||
|
}
|
||
|
else if( $3 == "[Main]:" && $5 == "sigint_handler():" || $3 == "ERROR[Main]:" && ( $4 FS $5 ) == "stack traceback:" ) {
|
||
|
|
||
|
# 2018-07-10 06:47:46: [Main]: INFO: sigint_handler(): Ctrl-C pressed, shutting down.
|
||
|
# 2018-07-10 16:18:52: ERROR[Main]: stack traceback:
|
||
|
print_info( "debug.txt", $1 " @disconnect " ( $4 == "stack" ? "(err)" : "(sig)" ) );
|
||
|
|
||
|
print get_timestamp( cur_date_str, cur_time_str ), LOG_STOPPED > ( world_path journal_file );
|
||
|
is_started = 0;
|
||
|
}
|
||
|
else if( $3 == "ACTION[Server]:" && ( $5 == "joins" || $6 == "joins" || $5 == "leaves" || $5 == "times" || $5 == "shuts" || $4 == "Server:" ) ) { # optimization hack
|
||
|
cur_action_str = substr( $0, 38 );
|
||
|
|
||
|
if( cur_action_str ~ /^[a-zA-Z0-9_-]+ \[[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\] joins game\./ ) {
|
||
|
|
||
|
# 2017-06-09 16:49:26: ACTION[Server]: sorcerykid [127.0.0.1] joins game.
|
||
|
# print_info( "debug.txt", $1 " @on_login_success " $4 FS trim( $5 ) );
|
||
|
|
||
|
if( check_user( $4 ) > 0 ) next;
|
||
|
print get_timestamp( cur_date_str, cur_time_str ), TX_LOGIN_SUCCESS, $4, trim( $5 ) > ( world_path journal_file );
|
||
|
}
|
||
|
else if( cur_action_str ~ /^Server: User [a-zA-Z0-9_-]+ at [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ supplied wrong password/ ) {
|
||
|
|
||
|
# 2017-06-09 20:35:20: ACTION[Server]: Server: User sorcerykid at 127.0.0.1 supplied wrong password (auth mechanism: SRP)
|
||
|
# print_info( "debug.txt", $1 " @on_login_failure " $6 FS $8 );
|
||
|
|
||
|
if( check_user( $6 ) > 0 ) next;
|
||
|
print get_timestamp( cur_date_str, cur_time_str ), TX_LOGIN_FAILURE, $6, $8 > ( world_path journal_file );
|
||
|
}
|
||
|
else if( cur_action_str ~ /^[a-zA-Z0-9_-]+ joins game\./ ) {
|
||
|
|
||
|
# 2017-06-09 16:49:26: ACTION[Server]: sorcerykid joins game. List of players:
|
||
|
# print_info( "debug.txt", $1 " @on_session_opened " $4 );
|
||
|
|
||
|
if( check_user( $4 ) > 0 ) next;
|
||
|
print get_timestamp( cur_date_str, cur_time_str ), TX_SESSION_OPENED, $4 > ( world_path journal_file );
|
||
|
}
|
||
|
else if( cur_action_str ~ /^[a-zA-Z0-9_-]+ leaves game\./ || cur_action_str ~ /[a-zA-Z0-9_-]+ times out\./ ) {
|
||
|
|
||
|
# 2017-06-09 20:32:32: ACTION[Server]: sorcerykid leaves game. List of players:
|
||
|
# 2017-06-09 20:34:47: ACTION[Server]: sorcerykid times out. List of players:
|
||
|
# print_info( "debug.txt", $1 " @on_session_closed " $4 );
|
||
|
|
||
|
if( check_user( $4 ) > 0 ) next;
|
||
|
print get_timestamp( cur_date_str, cur_time_str ), TX_SESSION_CLOSED, $4 > ( world_path journal_file );
|
||
|
}
|
||
|
else if( cur_action_str ~ /^[a-zA-Z0-9_-]+ shuts down server/ ) {
|
||
|
|
||
|
# 2017-06-09 20:32:32: ACTION[Server]: sorcerykid shuts down server
|
||
|
# print_info( "debug.txt", $1 " @disconnect (req)" );
|
||
|
|
||
|
print get_timestamp( cur_date_str, cur_time_str ), LOG_STOPPED > ( world_path journal_file );
|
||
|
is_started = 0;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
END {
|
||
|
total_orphans = 0;
|
||
|
for( name in db_users ) {
|
||
|
if( db_users[ name ] == 0 ) {
|
||
|
if( is_verbose == 1 ) {
|
||
|
print "WARNING: No player activity for '" name "' in debug.txt file.";
|
||
|
}
|
||
|
total_orphans++;
|
||
|
}
|
||
|
}
|
||
|
print "Total accounts in database: " total_records " (" total_orphans " orphaned accounts)";
|
||
|
print "Done!"
|
||
|
}
|