ModResult ModuleSpanningTree::HandleSquit(const std::vector<std::string>& parameters, User* user) { TreeServer* s = Utils->FindServerMask(parameters[0]); if (s) { if (s->IsRoot()) { user->WriteNotice("*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (" + parameters[0] + " matches local server name)"); return MOD_RES_DENY; } TreeSocket* sock = s->GetSocket(); if (s->IsLocal()) { ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0].c_str(),user->nick.c_str()); sock->Squit(s,"Server quit by " + user->GetFullRealHost()); ServerInstance->SE->DelFd(sock); sock->Close(); } else { user->WriteNotice("*** SQUIT may not be used to remove remote servers. Please use RSQUIT instead."); } } else { user->WriteNotice("*** SQUIT: The server \002" + parameters[0] + "\002 does not exist on the network."); } return MOD_RES_DENY; }
void ModuleSpanningTree::DoPingChecks(time_t curtime) { /* * Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data. * This prevents lost REMOTECONNECT notices */ long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000); restart: for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++) { TreeServer *s = i->second; // Skip myself if (s->IsRoot()) continue; // Do not ping servers that are not fully connected yet! // Servers which are connected to us have IsLocal() == true and if they're fully connected // then Socket->LinkState == CONNECTED. Servers that are linked to another server are always fully connected. if (s->IsLocal() && s->GetSocket()->GetLinkState() != CONNECTED) continue; // Now do PING checks on all servers // Only ping if this server needs one if (curtime >= s->NextPingTime()) { // And if they answered the last if (s->AnsweredLastPing()) { // They did, send a ping to them s->SetNextPingTime(curtime + Utils->PingFreq); s->GetSocket()->WriteLine(CmdBuilder("PING").push(s->GetID())); s->LastPingMsec = ts; } else { // They didn't answer the last ping, if they are locally connected, get rid of them. if (s->IsLocal()) { TreeSocket* sock = s->GetSocket(); sock->SendError("Ping timeout"); sock->Close(); goto restart; } } } // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping... if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing())) { /* The server hasnt responded, send a warning to opers */ ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", s->GetName().c_str(), Utils->PingWarnTime); s->Warned = true; } } }
void ModuleSpanningTree::OnUnloadModule(Module* mod) { if (!Utils) return; ServerInstance->PI->SendMetaData("modules", "-" + mod->ModuleSourceFile); if (mod == this) { // We are being unloaded, inform modules about all servers splitting which cannot be done later when the servers are actually disconnected const server_hash& servers = Utils->serverlist; for (server_hash::const_iterator i = servers.begin(); i != servers.end(); ++i) { TreeServer* server = i->second; if (!server->IsRoot()) FOREACH_MOD_CUSTOM(GetEventProvider(), ServerEventListener, OnServerSplit, (server)); } return; } // Some other module is being unloaded. If it provides an IOHook we use, we must close that server connection now. restart: // Close all connections which use an IO hook provided by this module const TreeServer::ChildServers& list = Utils->TreeRoot->GetChildren(); for (TreeServer::ChildServers::const_iterator i = list.begin(); i != list.end(); ++i) { TreeSocket* sock = (*i)->GetSocket(); if (sock->GetModHook(mod)) { sock->SendError("SSL module unloaded"); sock->Close(); // XXX: The list we're iterating is modified by TreeServer::SQuit() which is called by Close() goto restart; } } for (SpanningTreeUtilities::TimeoutList::const_iterator i = Utils->timeoutlist.begin(); i != Utils->timeoutlist.end(); ++i) { TreeSocket* sock = i->first; if (sock->GetModHook(mod)) sock->Close(); } }
// Ok, prepare to be confused. // After much mulling over how to approach this, it struck me that // the 'usual' way of doing a /MAP isnt the best way. Instead of // keeping track of a ton of ascii characters, and line by line // under recursion working out where to place them using multiplications // and divisons, we instead render the map onto a backplane of characters // (a character matrix), then draw the branches as a series of "L" shapes // from the nodes. This is not only friendlier on CPU it uses less stack. CmdResult CommandMap::Handle(const std::vector<std::string>& parameters, User* user) { if (parameters.size() > 0) { /* Remote MAP, the server is within the 1st parameter */ TreeServer* s = Utils->FindServerMask(parameters[0]); if (!s) { user->WriteNumeric(ERR_NOSUCHSERVER, "%s :No such server", parameters[0].c_str()); return CMD_FAILURE; } if (!s->IsRoot()) return CMD_SUCCESS; } // These arrays represent a virtual screen which we will // "scratch" draw to, as the console device of an irc // client does not provide for a proper terminal. int totusers = ServerInstance->Users->clientlist->size(); int totservers = Utils->serverlist.size(); int maxnamew = 0; int line = 0; char* names = new char[totservers * 100]; char* stats = new char[totservers * 50]; // The only recursive bit is called here. ShowMap(Utils->TreeRoot,user,0,line,names,maxnamew,stats); // Process each line one by one. for (int l = 1; l < line; l++) { char* myname = names + 100 * l; // scan across the line looking for the start of the // servername (the recursive part of the algorithm has placed // the servers at indented positions depending on what they // are related to) int first_nonspace = 0; while (myname[first_nonspace] == ' ') { first_nonspace++; } first_nonspace--; // Draw the `- (corner) section: this may be overwritten by // another L shape passing along the same vertical pane, becoming // a |- (branch) section instead. myname[first_nonspace] = '-'; myname[first_nonspace-1] = '`'; int l2 = l - 1; // Draw upwards until we hit the parent server, causing possibly // other corners (`-) to become branches (|-) while ((names[l2 * 100 + first_nonspace-1] == ' ') || (names[l2 * 100 + first_nonspace-1] == '`')) { names[l2 * 100 + first_nonspace-1] = '|'; l2--; } } float avg_users = totusers * 1.0 / line; for (int t = 0; t < line; t++) { // terminate the string at maxnamew characters names[100 * t + maxnamew] = '\0'; user->SendText(":%s %03d %s :%s %s", ServerInstance->Config->ServerName.c_str(), RPL_MAP, user->nick.c_str(), names + 100 * t, stats + 50 * t); } user->SendText(":%s %03d %s :%d server%s and %d user%s, average %.2f users per server", ServerInstance->Config->ServerName.c_str(), RPL_MAPUSERS, user->nick.c_str(), line, (line > 1 ? "s" : ""), totusers, (totusers > 1 ? "s" : ""), avg_users); user->SendText(":%s %03d %s :End of /MAP", ServerInstance->Config->ServerName.c_str(), RPL_ENDMAP, user->nick.c_str()); delete[] names; delete[] stats; return CMD_SUCCESS; }