Backported graceful restart feature to server

This commit is contained in:
Pauli Nieminen 2008-05-09 08:58:47 +00:00
parent 69c5a288cf
commit c57f1ac24e
8 changed files with 116 additions and 9 deletions

View file

@ -5,6 +5,7 @@ Version 1.4.2+svn:
* starting a campaing without any installed now gives an error.
* fixed issues with campaign info in non-compressed saved games
(bug #11386)
* Backported gracefull restart feature to server
* fixed an alignement issue which caused a SIGBUS on a Sparc
(debian bug #426318)
* added some includes to fix compilation problems with Sun Studio 12

View file

@ -1082,6 +1082,11 @@ void connect::process_network_data(const config& data, const network::connection
{
ui::process_network_data(data, sock);
if(data.child("leave_game")) {
set_result(QUIT);
return;
}
if (!data["side_drop"].empty()) {
const int side_drop = lexical_cast_default<int>(data["side_drop"], 0) - 1;
if(side_drop >= 0 && side_drop < int(sides_.size())) {

View file

@ -160,6 +160,7 @@ static void check_timeout()
// Reset last_ping if we didn't check for the last 10s.
if (last_ping_check + 10 <= now) last_ping = now;
if (static_cast<time_t>(last_ping + network::ping_timeout) <= now) {
int timeout = now - last_ping;
ERR_NW << "No server ping since " << timeout
<< " seconds. Connection timed out.\n";
@ -291,10 +292,16 @@ server_manager::server_manager(int port, CREATE_SERVER create_server) : free_(fa
}
server_manager::~server_manager()
{
stop();
}
void server_manager::stop()
{
if(free_) {
SDLNet_TCP_Close(server_socket);
server_socket = 0;
free_ = false;
}
}
@ -448,8 +455,10 @@ void connect_operation::run()
while(!notify_finished());
}
} // end namespace
connection connect(const std::string& host, int port)
{
connect_operation op(host,port);

View file

@ -79,6 +79,7 @@ struct server_manager {
~server_manager();
bool is_running() const;
void stop();
private:
bool free_;

View file

@ -51,10 +51,18 @@ input_stream::input_stream(const std::string& path) : fd_(-1), path_(path)
input_stream::~input_stream()
{
#ifndef _WIN32
stop();
#endif
}
void input_stream::stop()
{
#ifndef _WIN32
if(fd_ != -1) {
close(fd_);
unlink(path_.c_str());
fd_ = -1;
}
#endif
}

View file

@ -25,6 +25,7 @@ public:
~input_stream();
bool read_line(std::string& str);
void stop();
private:
input_stream(const input_stream&);

View file

@ -224,7 +224,7 @@ private:
void send_error(network::connection sock, const char* msg) const;
void send_error_dup(network::connection sock, const std::string& msg) const;
const network::manager net_manager_;
const network::server_manager server_;
network::server_manager server_;
//! std::map<network::connection,player>
player_map players_;
@ -252,6 +252,8 @@ private:
size_t default_max_messages_;
size_t default_time_period_;
size_t concurrent_connections_;
bool gracefull_restart;
std::string restart_command;
//! Parse the server config into local variables.
void load_config();
@ -289,6 +291,8 @@ private:
void delete_game(std::vector<game*>::iterator game_it);
void update_game_in_lobby(const game* g, network::connection exclude=0);
void start_new_server();
};
server::server(int port, input_stream& input, const std::string& config_file, size_t min_threads,size_t max_threads)
@ -299,6 +303,7 @@ server::server(int port, input_stream& input, const std::string& config_file, si
input_(input),
config_file_(config_file),
cfg_(read_config()),
gracefull_restart(false),
version_query_response_("[version]\n[/version]\n", simple_wml::INIT_COMPRESSED),
login_response_("[mustlogin]\n[/mustlogin]\n", simple_wml::INIT_COMPRESSED),
join_lobby_response_("[join_lobby]\n[/join_lobby]\n", simple_wml::INIT_COMPRESSED),
@ -373,6 +378,10 @@ void server::load_config() {
lexical_cast_default<size_t>(cfg_["messages_time_period"],10);
concurrent_connections_ =
lexical_cast_default<size_t>(cfg_["connections_allowed"],5);
// Example config line:
// restart_command="./wesnothd-debug -d -c ~/.wesnoth1.5/server.cfg"
// remember to make new one as a daemon or it will block old one
restart_command = cfg_["restart_command"];
accepted_versions_.clear();
const std::string& versions = cfg_["versions_accepted"];
@ -443,9 +452,22 @@ void server::dump_stats(const time_t& now) {
}
void server::run() {
int gracefull_counter = 0;
for (int loop = 0;; ++loop) {
SDL_Delay(20);
try {
// We are going to waith 10 seconds before shutting down so users can get out of game.
if (gracefull_restart && games_.size() == 0 && ++gracefull_counter > 500 )
{
// TODO: We should implement client side autoreconnect.
// Idea:
// server should send [reconnect]host=host,port=number[/reconnect]
// Then client would reconnect to new server automaticaly.
// This would also allow server to move to new port or address if there is need
lobby_.send_server_message("Server is restarting. You can connect to new server now.");
throw network::error("shut down");
}
if (config_reload == 1) {
cfg_ = read_config();
load_config();
@ -553,7 +575,7 @@ void server::run() {
{
network::disconnect(pl->first);
}
std::cout << "Shutting server down.\n";
LOG_SERVER << "Shutting server down.\n";
break;
}
if (!e.socket) {
@ -836,6 +858,18 @@ void server::process_query(const network::connection sock,
lobby_.send_server_message(response.str().c_str(), sock);
}
void server::start_new_server() {
if (restart_command.empty())
return;
// Example config line:
// restart_command="./wesnothd-debug -d -c ~/.wesnoth1.5/server.cfg"
// remember to make new one as a daemon or it will block old one
std::system(restart_command.c_str());
LOG_SERVER << "New server started with command: " << restart_command << "\n";
}
std::string server::process_command(const std::string& query) {
std::ostringstream out;
const std::string::const_iterator i = std::find(query.begin(),query.end(),' ');
@ -845,9 +879,41 @@ std::string server::process_command(const std::string& query) {
const std::string& help_msg = "Available commands are: ban(s) [<mask>] [<reason>],"
"kick <mask>, k(ick)ban [<mask>] [<reason>], help, metrics, netstats,"
" (lobby)msg <message>, motd [<message>], status [<mask>],"
" unban <ipmask>";
" unban <ipmask>, shut_down [now], restart";
if (command == "shut_down") {
throw network::error("shut down");
if (parameters == "now")
throw network::error("shut down");
else
{
// Gracefull shut down
server_.stop();
input_.stop();
gracefull_restart = true;
process_command("msg Server is shuting down. No more new games.");
out << "Server is doing gracefull shutdown\n";
}
#ifndef _WIN32 // Not sure if this works on windows
// TODO: check if this works in windows.
} else if (command == "restart") {
if (restart_command.empty())
{
out << "Server isn't configured for restart\n";
}
else
{
LOG_SERVER << "Gracefull restart requested\n";
gracefull_restart = true;
// stop listening socket
server_.stop();
input_.stop();
// start new server
start_new_server();
process_command("msg Server has been restarted. You can continue playing but new players cannot join this server.");
out << "New server strated.\n";
}
#endif
} else if (command == "help") {
out << help_msg;
} else if (command == "metrics") {
@ -1224,6 +1290,7 @@ void server::process_data_game(const network::connection sock,
lobby_.send_server_message(msg.str().c_str(), sock);
return;
}
// If this game is having its level data initialized
// for the first time, and is ready for players to join.
// We should currently have a summary of the game in g->level().
@ -1302,6 +1369,11 @@ void server::process_data_game(const network::connection sock,
make_add_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", diff);
lobby_.send_data(diff);
if (gracefull_restart)
{
delete_game(itor);
lobby_.send_server_message("You aren't allowed to make new game because server has been resarted. Please, reconnect to new server.", sock);
}
//! @todo FIXME: Why not save the level data in the history_?
return;
// Everything below should only be processed if the game is already intialized.
@ -1551,7 +1623,7 @@ void server::delete_game(std::vector<game*>::iterator game_it) {
gamelist->remove_child("game", index);
} else {
// Can happen when the game ends before the scenario was transfered.
DBG_SERVER << "Could not find game (" << (*game_it)->id()
LOG_SERVER << "Could not find game (" << (*game_it)->id()
<< ") to delete in games_and_users_list_.\n";
}
const user_vector& users = (*game_it)->all_game_users();

View file

@ -1,4 +1,10 @@
#!/bin/sh
#
#Example wesnothd.cfg
#versions_accepted="1.5.0+svn"
#restart_command="/home/user/source/trunk/utils/mp-server/run_server 1.5 &"
#
if [ $# -lt 1 ]; then
echo "Syntax: $0 <server version> [<additional parameters for wesnothd>]"
exit 1
@ -29,9 +35,6 @@ trap "$HOME/bin/send_server_message $SERVER; sleep 2; $HOME/bin/send_server_comm
while [ true ]
do
if ps -C wesnothd-$SERVER >/dev/null; then
sleep 10
else
DATE=$(date +"%Y%m%d-%H%M%S")
PORT=$(cat $SERVERBASE/port)
REV=$(ls -l $SERVERBASE/build | sed -e 's,.*wesnothd-\(svn-.*\)_.*/,\1,')
@ -55,6 +58,13 @@ do
fi
# wait for the server to terminate
wait $PID
EXIT_CODE=$?
echo "wesnoth exited with code: ${EXIT_CODE}"
cd
fi
# check for return code if not zero server should be restarted
if [ "$EXIT_CODE" = "0" ]; then
echo "script shutting down"
exit 0
fi
done