mirror of
https://github.com/anope/anope.git
synced 2026-06-12 17:04:47 +02:00
286 lines
8.2 KiB
C++
286 lines
8.2 KiB
C++
// Anope IRC Services <https://www.anope.org/>
|
|
//
|
|
// Copyright (C) 2003-2026 Anope Contributors
|
|
//
|
|
// Anope is free software. You can use, modify, and/or distribute it under the
|
|
// terms of version 2 of the GNU General Public License. See docs/LICENSE.txt
|
|
// for the complete terms of this license and docs/AUTHORS.txt for a list of
|
|
// contributors.
|
|
//
|
|
// Based on the original code of Epona by Lara
|
|
// Based on the original code of Services by Andy Church
|
|
//
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
#include "module.h"
|
|
#include "modules/operserv/session.h"
|
|
|
|
struct Stats final
|
|
: Serializable
|
|
{
|
|
static Stats *me;
|
|
|
|
Stats() : Serializable("Stats")
|
|
{
|
|
if (!me)
|
|
me = this;
|
|
}
|
|
};
|
|
|
|
struct StatsType final
|
|
: Serialize::Type
|
|
{
|
|
StatsType()
|
|
: Serialize::Type("Stats")
|
|
{
|
|
}
|
|
|
|
void Serialize(Serializable *obj, Serialize::Data &data) const override
|
|
{
|
|
data.Store("maxusercnt", MaxUserCount);
|
|
data.Store("maxusertime", MaxUserTime);
|
|
}
|
|
|
|
Serializable *Unserialize(Serializable *obj, Serialize::Data &data) const override
|
|
{
|
|
MaxUserCount = data.Load<size_t>("maxusercnt");
|
|
MaxUserTime = data.Load<time_t>("maxusertime");
|
|
return Stats::me;
|
|
}
|
|
};
|
|
|
|
Stats *Stats::me = nullptr;
|
|
|
|
class CommandOSStats final
|
|
: public Command
|
|
{
|
|
private:
|
|
ServiceReference<XLineManager> akills, snlines, sqlines;
|
|
|
|
static void ReportXLineStats(CommandSource &source, XLineManager *xlm, const char *type, const char *config)
|
|
{
|
|
source.Reply(_("Current number of %ss: \002%zu\002"), type, xlm->GetCount());
|
|
|
|
const auto timeout = Config->GetModule("operserv").Get<time_t>(config, "30d");
|
|
if (timeout)
|
|
source.Reply(_("Default %s expiry time: \002%s\002"), type, Anope::Duration(timeout, source.nc, true).c_str());
|
|
else
|
|
source.Reply(_("Default %s expiry time: \002No expiration\002"), type);
|
|
}
|
|
private:
|
|
void DoStatsAkill(CommandSource &source)
|
|
{
|
|
if (akills)
|
|
ReportXLineStats(source, *akills, "AKILL", "autokillexpiry");
|
|
|
|
if (snlines)
|
|
ReportXLineStats(source, *snlines, "SNLINE", "snlineexpiry");
|
|
|
|
if (sqlines)
|
|
ReportXLineStats(source, *sqlines, "SQLINE", "sqlineexpiry");
|
|
}
|
|
|
|
static void DoStatsReset(CommandSource &source)
|
|
{
|
|
MaxUserCount = UserListByNick.size();
|
|
MaxUserTime = Anope::CurTime;
|
|
Stats::me->QueueUpdate();
|
|
source.Reply(_("Statistics reset."));
|
|
}
|
|
|
|
static void DoStatsUptime(CommandSource &source)
|
|
{
|
|
time_t uptime = Anope::CurTime - Anope::StartTime;
|
|
source.Reply(_("Current users: \002%zu\002 (\002%zu\002 ops)"), UserListByNick.size(), OperCount);
|
|
source.Reply(_("Maximum users: \002%zu\002 (%s)"), MaxUserCount, Anope::strftime(MaxUserTime, source.GetAccount()).c_str());
|
|
source.Reply(_("Services up %s."), Anope::Duration(uptime, source.GetAccount()).c_str());
|
|
}
|
|
|
|
static void DoStatsUplink(CommandSource &source)
|
|
{
|
|
Anope::string buf;
|
|
for (const auto &capab : Servers::Capab)
|
|
buf += " " + capab;
|
|
if (!buf.empty())
|
|
buf.erase(buf.begin());
|
|
|
|
source.Reply(_("Uplink server: %s"), Me->GetLinks().front()->GetName().c_str());
|
|
source.Reply(_("Uplink capab: %s"), buf.c_str());
|
|
source.Reply(_("Servers found: %zu"), Servers::ByName.size() - 1);
|
|
}
|
|
|
|
template<typename T> void GetHashStats(const T &map, size_t &entries, size_t &buckets, size_t &max_chain)
|
|
{
|
|
entries = map.size(), buckets = map.bucket_count(), max_chain = 0;
|
|
for (size_t i = 0; i < buckets; ++i)
|
|
if (map.bucket_size(i) > max_chain)
|
|
max_chain = map.bucket_size(i);
|
|
}
|
|
|
|
void DoStatsHash(CommandSource &source)
|
|
{
|
|
size_t entries, buckets, max_chain;
|
|
|
|
GetHashStats(UserListByNick, entries, buckets, max_chain);
|
|
source.Reply(_("Users (nick): %lu entries, %lu buckets, longest chain is %zu"), entries, buckets, max_chain);
|
|
|
|
if (!UserListByUID.empty())
|
|
{
|
|
GetHashStats(UserListByUID, entries, buckets, max_chain);
|
|
source.Reply(_("Users (uid): %lu entries, %lu buckets, longest chain is %zu"), entries, buckets, max_chain);
|
|
}
|
|
|
|
GetHashStats(ChannelList, entries, buckets, max_chain);
|
|
source.Reply(_("Channels: %zu entries, %zu buckets, longest chain is %zu"), entries, buckets, max_chain);
|
|
|
|
GetHashStats(*RegisteredChannelList, entries, buckets, max_chain);
|
|
source.Reply(_("Registered channels: %zu entries, %zu buckets, longest chain is %zu"), entries, buckets, max_chain);
|
|
|
|
GetHashStats(*NickAliasList, entries, buckets, max_chain);
|
|
source.Reply(_("Registered nicknames: %zu entries, %zu buckets, longest chain is %zu"), entries, buckets, max_chain);
|
|
|
|
GetHashStats(*NickCoreList, entries, buckets, max_chain);
|
|
source.Reply(_("Registered accounts: %zu entries, %zu buckets, longest chain is %zu"), entries, buckets, max_chain);
|
|
|
|
if (OperServ::session_service)
|
|
{
|
|
GetHashStats(OperServ::session_service->GetSessions(), entries, buckets, max_chain);
|
|
source.Reply(_("Sessions: %zu entries, %zu buckets, longest chain is %zu"), entries, buckets, max_chain);
|
|
}
|
|
}
|
|
|
|
void DoStatsPassword(CommandSource &source)
|
|
{
|
|
Anope::map<size_t> counts;
|
|
size_t missing = 0;
|
|
size_t unknown = 0;
|
|
for (const auto &[_, nc] : *NickCoreList)
|
|
{
|
|
if (nc->pass.empty())
|
|
{
|
|
missing++;
|
|
continue;
|
|
}
|
|
|
|
auto sep = nc->pass.find(':');
|
|
if (sep == Anope::string::npos)
|
|
{
|
|
unknown++;
|
|
continue;
|
|
}
|
|
|
|
counts[nc->pass.substr(0, sep)]++;
|
|
}
|
|
|
|
for (const auto &[algo, count] : counts)
|
|
source.Reply(_("Passwords encrypted with %s: %zu"), algo.c_str(), count);
|
|
if (missing)
|
|
source.Reply(_("Missing passwords: %zu"), missing);
|
|
if (unknown)
|
|
source.Reply(_("Unknown passwords: %zu"), unknown);
|
|
}
|
|
|
|
public:
|
|
CommandOSStats(Module *creator) : Command(creator, "operserv/stats", 0, 1),
|
|
akills("XLineManager", "xlinemanager/sgline"), snlines("XLineManager", "xlinemanager/snline"), sqlines("XLineManager", "xlinemanager/sqline")
|
|
{
|
|
this->SetDesc(_("Show status of services and network"));
|
|
this->SetSyntax("[AKILL | HASH | PASSWORD | UPLINK | UPTIME | ALL | RESET]");
|
|
}
|
|
|
|
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
|
{
|
|
Anope::string extra = !params.empty() ? params[0] : "";
|
|
|
|
Log(LOG_ADMIN, source, this) << extra;
|
|
|
|
if (extra.equals_ci("RESET"))
|
|
return this->DoStatsReset(source);
|
|
|
|
bool handled = false;
|
|
if (extra.equals_ci("ALL") || extra.equals_ci("AKILL"))
|
|
{
|
|
this->DoStatsAkill(source);
|
|
handled = true;
|
|
}
|
|
|
|
if (extra.equals_ci("ALL") || extra.equals_ci("HASH"))
|
|
{
|
|
this->DoStatsHash(source);
|
|
handled = true;
|
|
}
|
|
|
|
if (extra.equals_ci("ALL") || extra.equals_ci("PASSWORD"))
|
|
{
|
|
this->DoStatsPassword(source);
|
|
handled = true;
|
|
}
|
|
|
|
if (extra.equals_ci("ALL") || extra.equals_ci("UPLINK"))
|
|
{
|
|
this->DoStatsUplink(source);
|
|
handled = true;
|
|
}
|
|
|
|
if (extra.empty() || extra.equals_ci("ALL") || extra.equals_ci("UPTIME"))
|
|
{
|
|
this->DoStatsUptime(source);
|
|
handled = true;
|
|
}
|
|
|
|
if (!handled)
|
|
source.Reply(_("Unknown STATS option: \002%s\002"), extra.c_str());
|
|
}
|
|
|
|
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
|
|
{
|
|
this->SendSyntax(source);
|
|
source.Reply(" ");
|
|
source.Reply(_(
|
|
"Without any option, shows the current number of users online, "
|
|
"and the highest number of users online since services was "
|
|
"started, and the length of time services has been running."
|
|
"\n\n"
|
|
"With the \002AKILL\002 option, displays the current size of the "
|
|
"AKILL list and the current default expiry time."
|
|
"\n\n"
|
|
"The \002RESET\002 option currently resets the maximum user count "
|
|
"to the number of users currently present on the network."
|
|
"\n\n"
|
|
"The \002PASSWORD\002 option displays the encryption algorithms used "
|
|
"for user passwords. "
|
|
"\n\n"
|
|
"The \002UPLINK\002 option displays information about the current "
|
|
"server Anope uses as an uplink to the network."
|
|
"\n\n"
|
|
"The \002HASH\002 option displays information about the hash maps."
|
|
"\n\n"
|
|
"The \002ALL\002 option displays all of the above statistics."
|
|
));
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class OSStats final
|
|
: public Module
|
|
{
|
|
CommandOSStats commandosstats;
|
|
StatsType stats_type;
|
|
Stats stats_saver;
|
|
|
|
public:
|
|
OSStats(const Anope::string &modname, const Anope::string &creator)
|
|
: Module(modname, creator, VENDOR)
|
|
, commandosstats(this)
|
|
{
|
|
}
|
|
|
|
void OnUserConnect(User *u, bool &exempt) override
|
|
{
|
|
if (UserListByNick.size() == MaxUserCount && Anope::CurTime == MaxUserTime)
|
|
Stats::me->QueueUpdate();
|
|
}
|
|
};
|
|
|
|
MODULE_INIT(OSStats)
|