various server command improvements
* added the banned nick to a ban when available * made the sample command socket only and have it output the current value when no parameter is given * made the status command report for all nicks with the same IP when using a simple nick as parameter (default for ordinary users) * merged the samples command back into metrics * actually allowed advertised commands for ordinary users * removed a couple of extraneous newlines from command output * fixed an improper name for a local variable
This commit is contained in:
parent
c60c921864
commit
f64062ef7b
5 changed files with 119 additions and 78 deletions
|
@ -34,12 +34,12 @@ namespace wesnothd {
|
|||
|
||||
std::ostream& operator<<(std::ostream& o, const banned& n)
|
||||
{
|
||||
return o << "IP: " << n.get_ip() <<
|
||||
" reason: '" << n.get_reason() <<
|
||||
"' end_time: " << n.get_human_end_time() <<
|
||||
" start_time: " << n.get_human_start_time() <<
|
||||
" issuer: " << n.get_who_banned();
|
||||
|
||||
return o << "IP: " << n.get_ip() <<
|
||||
(n.get_nick().empty() ? "" : " nick: " + n.get_nick()) <<
|
||||
" reason: '" << n.get_reason() << "'"
|
||||
"\nstart_time: " << n.get_human_start_time() <<
|
||||
" end_time: " << n.get_human_end_time() <<
|
||||
" issuer: " << n.get_who_banned();
|
||||
}
|
||||
|
||||
bool banned_compare::operator()(const banned_ptr& a, const banned_ptr& b) const
|
||||
|
@ -115,13 +115,15 @@ namespace wesnothd {
|
|||
const time_t end_time,
|
||||
const std::string& reason,
|
||||
const std::string& who_banned,
|
||||
const std::string& group) :
|
||||
const std::string& group,
|
||||
const std::string& nick) :
|
||||
ip_text_(ip),
|
||||
end_time_(end_time),
|
||||
start_time_(time(0)),
|
||||
reason_(reason),
|
||||
who_banned_(who_banned),
|
||||
group_(group)
|
||||
group_(group),
|
||||
nick_(nick)
|
||||
{
|
||||
ip_mask pair = parse_ip(ip_text_);
|
||||
ip_ = pair.first;
|
||||
|
@ -191,7 +193,7 @@ namespace wesnothd {
|
|||
ip_ = pair.first;
|
||||
mask_ = pair.second;
|
||||
}
|
||||
|
||||
nick_ = cfg["nick"];
|
||||
if (cfg.has_attribute("end_time"))
|
||||
end_time_ = lexical_cast_default<time_t>(cfg["end_time"], 0);
|
||||
if (cfg.has_attribute("start_time"))
|
||||
|
@ -208,6 +210,7 @@ namespace wesnothd {
|
|||
void banned::write(config& cfg) const
|
||||
{
|
||||
cfg["ip"] = get_ip();
|
||||
cfg["nick"] = get_nick();
|
||||
if (end_time_ > 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
@ -448,7 +451,8 @@ namespace wesnothd {
|
|||
const time_t& end_time,
|
||||
const std::string& reason,
|
||||
const std::string& who_banned,
|
||||
const std::string& group)
|
||||
const std::string& group,
|
||||
const std::string& nick)
|
||||
{
|
||||
try {
|
||||
ban_set::iterator ban;
|
||||
|
@ -464,7 +468,7 @@ namespace wesnothd {
|
|||
}
|
||||
std::ostringstream ret;
|
||||
try {
|
||||
banned_ptr new_ban(new banned(ip, end_time, reason,who_banned, group));
|
||||
banned_ptr new_ban(new banned(ip, end_time, reason,who_banned, group, nick));
|
||||
bans_.insert(new_ban);
|
||||
if (end_time != 0)
|
||||
time_queue_.push(new_ban);
|
||||
|
@ -564,7 +568,7 @@ namespace wesnothd {
|
|||
out << "No bans set.";
|
||||
return;
|
||||
}
|
||||
|
||||
out << "BAN LIST\n";
|
||||
std::set<std::string> groups;
|
||||
|
||||
for (ban_set::const_iterator i = bans_.begin();
|
||||
|
@ -626,7 +630,7 @@ namespace wesnothd {
|
|||
}
|
||||
ban_help_ += "ban 127.0.0.1 2h20m flooded lobby\n"
|
||||
"kban suokko 5D flooded again\n"
|
||||
"kban suokko Y One year ban for constant flooding\n";
|
||||
"kban suokko Y One year ban for constant flooding";
|
||||
}
|
||||
|
||||
void ban_manager::load_config(const config& cfg)
|
||||
|
|
|
@ -71,6 +71,7 @@ namespace wesnothd {
|
|||
std::string reason_;
|
||||
std::string who_banned_;
|
||||
std::string group_;
|
||||
std::string nick_;
|
||||
static const std::string who_banned_default_;
|
||||
typedef std::pair<unsigned int, unsigned int> ip_mask;
|
||||
|
||||
|
@ -79,7 +80,7 @@ namespace wesnothd {
|
|||
banned(const std::string& ip);
|
||||
|
||||
public:
|
||||
banned(const std::string& ip, const time_t end_time, const std::string& reason, const std::string& who_banned=who_banned_default_, const std::string& group="");
|
||||
banned(const std::string& ip, const time_t end_time, const std::string& reason, const std::string& who_banned=who_banned_default_, const std::string& group="", const std::string& nick="");
|
||||
banned(const config&);
|
||||
|
||||
void read(const config&);
|
||||
|
@ -103,6 +104,9 @@ namespace wesnothd {
|
|||
std::string get_who_banned() const
|
||||
{ return who_banned_; }
|
||||
|
||||
std::string get_nick() const
|
||||
{ return nick_; }
|
||||
|
||||
bool match_group(const std::string& group) const
|
||||
{ return group_ == group; }
|
||||
|
||||
|
@ -148,7 +152,7 @@ namespace wesnothd {
|
|||
|
||||
time_t parse_time(std::string time_in) const;
|
||||
|
||||
std::string ban(const std::string&, const time_t&, const std::string&, const std::string&, const std::string&);
|
||||
std::string ban(const std::string&, const time_t&, const std::string&, const std::string&, const std::string&, const std::string& = "");
|
||||
void unban(std::ostringstream& os, const std::string& ip);
|
||||
void unban_group(std::ostringstream& os, const std::string& group);
|
||||
|
||||
|
|
|
@ -104,36 +104,15 @@ void metrics::game_terminated(const std::string& reason)
|
|||
|
||||
std::ostream& metrics::games(std::ostream& out)
|
||||
{
|
||||
if (terminations_.empty()) return out;
|
||||
if (terminations_.empty()) return out << "No game ended so far.";
|
||||
|
||||
size_t n = 0;
|
||||
out << "Games have been terminated in the following ways: \n";
|
||||
out << "Games have been terminated in the following ways:\n";
|
||||
for(std::map<std::string,int>::const_iterator i = terminations_.begin(); i != terminations_.end(); ++i) {
|
||||
out << i->first << ": " << i->second << "\n";
|
||||
++n;
|
||||
}
|
||||
out << "Total number of games = " << n;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& metrics::samples(std::ostream& out)
|
||||
{
|
||||
if (samples_.empty()) return out;
|
||||
|
||||
std::vector<metrics::sample> ordered_samples = samples_;
|
||||
std::sort(ordered_samples.begin(), ordered_samples.end(), compare_samples_by_time());
|
||||
|
||||
out << "Request types:\n";
|
||||
|
||||
size_t n = 0;
|
||||
for(std::vector<metrics::sample>::const_iterator s = ordered_samples.begin(); s != ordered_samples.end(); ++s) {
|
||||
out << "'" << s->name << "' called " << s->nsamples << " times "
|
||||
<< s->parsing_time << "("<< s->max_parsing_time <<") parsing time, "
|
||||
<< s->processing_time << "("<<s->max_processing_time<<") processing time\n";
|
||||
++n;
|
||||
}
|
||||
out << "Total number of games = " << n;
|
||||
out << "Total number of finished games = " << n;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
@ -151,8 +130,30 @@ std::ostream& operator<<(std::ostream& out, metrics& met)
|
|||
<< minutes << " minutes, " << seconds << " seconds\n"
|
||||
<< met.nrequests_ << " requests serviced. " << requests_immediate
|
||||
<< " (" << percent_immediate << "%) "
|
||||
<< " requests were serviced immediately\n"
|
||||
<< "longest burst of requests was " << met.most_consecutive_requests_;
|
||||
<< "requests were serviced immediately.\n"
|
||||
<< "longest burst of requests was: " << met.most_consecutive_requests_;
|
||||
|
||||
if (met.samples_.empty()) return out;
|
||||
|
||||
std::vector<metrics::sample> ordered_samples = met.samples_;
|
||||
std::sort(ordered_samples.begin(), ordered_samples.end(), compare_samples_by_time());
|
||||
|
||||
out << "\nSampled request types:\n";
|
||||
|
||||
size_t n = 0;
|
||||
size_t pa = 0;
|
||||
size_t pr = 0;
|
||||
for(std::vector<metrics::sample>::const_iterator s = ordered_samples.begin(); s != ordered_samples.end(); ++s) {
|
||||
out << "'" << s->name << "' called " << s->nsamples << " times "
|
||||
<< s->parsing_time << "("<< s->max_parsing_time <<") parsing time, "
|
||||
<< s->processing_time << "("<<s->max_processing_time<<") processing time\n";
|
||||
n += s->nsamples;
|
||||
pa += s->parsing_time;
|
||||
pr += s->processing_time;
|
||||
}
|
||||
out << "Total number of request samples = " << n << "\n"
|
||||
<< "Total parsing time = " << pa << "\n"
|
||||
<< "Total processing time = " << pr;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,6 @@ public:
|
|||
void game_terminated(const std::string& reason);
|
||||
|
||||
std::ostream& games(std::ostream& out);
|
||||
std::ostream& samples(std::ostream& out);
|
||||
friend std::ostream& operator<<(std::ostream& out, metrics& met);
|
||||
|
||||
struct sample {
|
||||
|
|
|
@ -846,7 +846,7 @@ void server::process_query(const network::connection sock,
|
|||
const simple_wml::string_span& command(query["type"]);
|
||||
std::ostringstream response;
|
||||
const std::string& help_msg = "Available commands are: help, games, metrics,"
|
||||
" motd, netstats [all], samples, stats, status, wml.";
|
||||
" motd, netstats [all], stats, status, wml.";
|
||||
if (admins_.count(sock) != 0) {
|
||||
LOG_SERVER << "Admin Command:" << "\ttype: " << command
|
||||
<< "\tIP: "<< network::ip_address(sock)
|
||||
|
@ -857,8 +857,16 @@ void server::process_query(const network::connection sock,
|
|||
response << help_msg;
|
||||
} else if (command == "status") {
|
||||
response << process_command(command.to_string() + " " + pl->second.name(), pl->second.name());
|
||||
} else if (command == "status " + pl->second.name() || command == "metrics"
|
||||
|| command == "motd" || command == "wml" || command == "netstats" || command == "netstats all") {
|
||||
} else if (command == "games"
|
||||
|| command == "metrics"
|
||||
|| command == "motd"
|
||||
|| command == "netstats"
|
||||
|| command == "netstats all"
|
||||
|| command == "sample"
|
||||
|| command == "stats"
|
||||
|| command == "status " + pl->second.name()
|
||||
|| command == "wml")
|
||||
{
|
||||
response << process_command(command.to_string(), pl->second.name());
|
||||
} else if (command == admin_passwd_) {
|
||||
LOG_SERVER << "New Admin recognized:" << "\tIP: "
|
||||
|
@ -896,11 +904,12 @@ std::string server::process_command(const std::string& query, const std::string&
|
|||
std::string parameters = (i == query.end() ? "" : std::string(i+1,query.end()));
|
||||
utils::strip(parameters);
|
||||
const std::string& help_msg = "Available commands are: ban <mask> [<time>] <reason>,"
|
||||
" bans [deleted], kick <mask>, k(ick)ban <mask> [<time>] <reason>,"
|
||||
" help, games, metrics, netstats, (lobby)msg <message>, motd [<message>],"
|
||||
" samples, stats, status [<mask>], unban <ipmask>";
|
||||
// Shutdown and restart commands can only be issued via the socket.
|
||||
if (command == "shut_down" && issuer_name == "*socket*") {
|
||||
" bans [deleted], kick <mask>, k[ick]ban <mask> [<time>] <reason>,"
|
||||
" help, games, metrics, netstats [all], [lobby]msg <message>, motd [<message>],"
|
||||
" requests, stats, status [<mask>], unban <ipmask>";
|
||||
// Shutdown, restart and sample commands can only be issued via the socket.
|
||||
if (command == "shut_down") {
|
||||
if (issuer_name != "*socket*") return "";
|
||||
if (parameters == "now") {
|
||||
throw network::error("shut down");
|
||||
} else {
|
||||
|
@ -914,7 +923,8 @@ std::string server::process_command(const std::string& query, const std::string&
|
|||
|
||||
// this works in windows if using "start /B"
|
||||
// like in wesnoth MP server start code
|
||||
} else if (command == "restart" && issuer_name == "*socket*") {
|
||||
} else if (command == "restart") {
|
||||
if (issuer_name != "*socket*") return "";
|
||||
if (restart_command.empty()) {
|
||||
out << "No restart_command configured! Not restarting.";
|
||||
} else {
|
||||
|
@ -928,16 +938,28 @@ std::string server::process_command(const std::string& query, const std::string&
|
|||
process_command("msg The server has been restarted. You may finish current games but can't start new ones and new players can't join this (old) server instance. (So if a player of your game disconnects you have to save, reconnect and reload the game on the new server instance. It is actually recommended to do that right away.)", issuer_name);
|
||||
out << "New server started.";
|
||||
}
|
||||
} else if (command == "sample") {
|
||||
if (parameters.empty()) {
|
||||
out << "Current sample frequency: " << request_sample_frequency;
|
||||
return out.str();
|
||||
} else if (issuer_name != "*socket*") {
|
||||
return "";
|
||||
}
|
||||
request_sample_frequency = atoi(parameters.c_str());
|
||||
if (request_sample_frequency <= 0) {
|
||||
out << "Sampling turned off.";
|
||||
} else {
|
||||
out << "Sampling every " << request_sample_frequency << " requests.";
|
||||
}
|
||||
} else if (command == "help") {
|
||||
out << help_msg;
|
||||
return help_msg;
|
||||
} else if (command == "stats") {
|
||||
out << "Number of games = " << games_.size()
|
||||
<< "\nTotal number of users = " << players_.size()
|
||||
<< "\nNumber of users in the lobby = " << lobby_.nobservers();
|
||||
return out.str();
|
||||
} else if (command == "metrics") {
|
||||
out << metrics_;
|
||||
} else if (command == "samples") {
|
||||
metrics_.samples(out);
|
||||
} else if (command == "games") {
|
||||
metrics_.games(out);
|
||||
} else if (command == "wml") {
|
||||
|
@ -962,22 +984,34 @@ std::string server::process_command(const std::string& query, const std::string&
|
|||
}
|
||||
}
|
||||
LOG_SERVER << "<server> " + parameters + "\n";
|
||||
out << "message '" << parameters << "' relayed to players\n";
|
||||
out << "message '" << parameters << "' relayed to players";
|
||||
} else if (command == "status") {
|
||||
out << "STATUS REPORT\n";
|
||||
out << "STATUS REPORT";
|
||||
// If a simple username is given we'll check for its IP instead.
|
||||
if (utils::isvalid_username(parameters)) {
|
||||
bool found = false;
|
||||
for (wesnothd::player_map::const_iterator pl = players_.begin(); pl != players_.end(); ++pl) {
|
||||
if (parameters == pl->second.name().c_str()) {
|
||||
parameters = network::ip_address(pl->first);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) return out.str();
|
||||
}
|
||||
for (wesnothd::player_map::const_iterator pl = players_.begin(); pl != players_.end(); ++pl) {
|
||||
if (parameters == ""
|
||||
|| utils::wildcard_string_match(pl->second.name(), parameters)
|
||||
|| utils::wildcard_string_match(network::ip_address(pl->first), parameters)) {
|
||||
|| utils::wildcard_string_match(network::ip_address(pl->first), parameters)
|
||||
|| utils::wildcard_string_match(pl->second.name(), parameters)) {
|
||||
const network::connection_stats& stats = network::get_connection_stats(pl->first);
|
||||
const int time_connected = stats.time_connected/1000;
|
||||
const int seconds = time_connected%60;
|
||||
const int minutes = (time_connected/60)%60;
|
||||
const int hours = time_connected/(60*60);
|
||||
out << "'" << pl->second.name() << "' @ " << network::ip_address(pl->first)
|
||||
out << "\n'" << pl->second.name() << "' @ " << network::ip_address(pl->first)
|
||||
<< " connected for " << hours << ":" << minutes << ":" << seconds
|
||||
<< " sent " << stats.bytes_sent << " bytes, received "
|
||||
<< stats.bytes_received << " bytes\n";
|
||||
<< stats.bytes_received << " bytes";
|
||||
}
|
||||
}
|
||||
} else if (command == "bans") {
|
||||
|
@ -987,7 +1021,7 @@ std::string server::process_command(const std::string& query, const std::string&
|
|||
ban_manager_.list_bans(out);
|
||||
}
|
||||
} else if (command == "ban" || command == "kban" || command == "kickban" || command == "gban") {
|
||||
bool banned_ = false;
|
||||
bool banned = false;
|
||||
const bool kick = (command == "kban" || command == "kickban");
|
||||
const bool group_ban = command == "gban";
|
||||
std::string::iterator first_space = std::find(parameters.begin(), parameters.end(), ' ');
|
||||
|
@ -1018,7 +1052,7 @@ std::string server::process_command(const std::string& query, const std::string&
|
|||
// if we find a '.' consider it an ip mask
|
||||
//! @todo FIXME: make a proper check for valid IPs
|
||||
if (std::count(target.begin(), target.end(), '.') >= 1) {
|
||||
banned_ = true;
|
||||
banned = true;
|
||||
|
||||
std::string err = ban_manager_.ban(target, parsed_time, reason, issuer_name, group);
|
||||
out << err;
|
||||
|
@ -1035,24 +1069,27 @@ std::string server::process_command(const std::string& query, const std::string&
|
|||
}
|
||||
}
|
||||
} else {
|
||||
bool kicked = false;
|
||||
for (wesnothd::player_map::const_iterator pl = players_.begin();
|
||||
pl != players_.end(); ++pl)
|
||||
{
|
||||
if (utils::wildcard_string_match(pl->second.name(), target)) {
|
||||
banned_ = true;
|
||||
banned = true;
|
||||
const std::string& ip = network::ip_address(pl->first);
|
||||
if (!is_ip_banned(ip)) {
|
||||
std::string err = ban_manager_.ban(ip,parsed_time, reason, issuer_name, group);
|
||||
std::string err = ban_manager_.ban(ip, parsed_time, reason, issuer_name, group, target);
|
||||
out << err;
|
||||
}
|
||||
if (kick) {
|
||||
if (kicked) out << "\n";
|
||||
else kicked = true;
|
||||
out << "\nKicked " << pl->second.name() << ".";
|
||||
send_error(pl->first, ("You have been banned. Reason: " + reason).c_str());
|
||||
network::queue_disconnect(pl->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!banned_) {
|
||||
if (!banned) {
|
||||
out << "Nickmask '" << target << "' did not match, no bans set.";
|
||||
}
|
||||
}
|
||||
|
@ -1063,7 +1100,7 @@ std::string server::process_command(const std::string& query, const std::string&
|
|||
ban_manager_.unban(out, parameters);
|
||||
} else if (command == "ungban") {
|
||||
if (parameters == "") {
|
||||
return "You must enter an ipmask to unban.";
|
||||
return "You must enter an ipmask to ungban.";
|
||||
}
|
||||
ban_manager_.unban_group(out, parameters);
|
||||
} else if (command == "kick") {
|
||||
|
@ -1077,8 +1114,10 @@ std::string server::process_command(const std::string& query, const std::string&
|
|||
pl != players_.end(); ++pl)
|
||||
{
|
||||
if (utils::wildcard_string_match(network::ip_address(pl->first), parameters)) {
|
||||
kicked = true;
|
||||
out << "Kicked " << pl->second.name() << ".\n";
|
||||
if (kicked) out << "\n";
|
||||
else kicked = true;
|
||||
out << "Kicked " << pl->second.name() << " ("
|
||||
<< network::ip_address(pl->first) << ").";
|
||||
send_error(pl->first, "You have been kicked.");
|
||||
network::queue_disconnect(pl->first);
|
||||
}
|
||||
|
@ -1088,22 +1127,16 @@ std::string server::process_command(const std::string& query, const std::string&
|
|||
pl != players_.end(); ++pl)
|
||||
{
|
||||
if (utils::wildcard_string_match(pl->second.name(), parameters)) {
|
||||
kicked = true;
|
||||
if (kicked) out << "\n";
|
||||
else kicked = true;
|
||||
out << "Kicked " << pl->second.name() << " ("
|
||||
<< network::ip_address(pl->first) << ").\n";
|
||||
<< network::ip_address(pl->first) << ").";
|
||||
send_error(pl->first, "You have been kicked.");
|
||||
network::queue_disconnect(pl->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!kicked) out << "No user matched '" << parameters << "'.\n";
|
||||
} else if(command == "sample") {
|
||||
request_sample_frequency = atoi(parameters.c_str());
|
||||
if(request_sample_frequency <= 0) {
|
||||
out << "Sampling turned off";
|
||||
} else {
|
||||
out << "Sampling every " << request_sample_frequency << " requests\n";
|
||||
}
|
||||
if (!kicked) out << "No user matched '" << parameters << "'.";
|
||||
} else if (command == "motd") {
|
||||
if (parameters == "") {
|
||||
if (motd_ != "") {
|
||||
|
|
Loading…
Add table
Reference in a new issue