]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/stats.cpp
Replace copyright headers with headers granting specific authors copyright
[user/henk/code/inspircd.git] / src / stats.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *
6  * This file is part of InspIRCd.  InspIRCd is free software: you can
7  * redistribute it and/or modify it under the terms of the GNU General Public
8  * License as published by the Free Software Foundation, version 2.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19
20 #include "inspircd.h"
21 #include "xline.h"
22 #include "commands/cmd_whowas.h"
23
24 void InspIRCd::DoStats(char statschar, User* user, string_list &results)
25 {
26         std::string sn(this->Config->ServerName);
27
28         bool isPublic = Config->UserStats.find(statschar) != std::string::npos;
29         bool isRemoteOper = IS_REMOTE(user) && IS_OPER(user);
30         bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex");
31
32         if (!isPublic && !isRemoteOper && !isLocalOperWithPrivs)
33         {
34                 this->SNO->WriteToSnoMask('t',
35                                 "%s '%c' denied for %s (%s@%s)",
36                                 (IS_LOCAL(user) ? "Stats" : "Remote stats"),
37                                 statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
38                 results.push_back(sn + " 481 " + user->nick + " :Permission denied - STATS " + statschar + " requires the servers/auspex priv.");
39                 return;
40         }
41
42         ModResult MOD_RESULT;
43         FIRST_MOD_RESULT(OnStats, MOD_RESULT, (statschar, user, results));
44         if (MOD_RESULT == MOD_RES_DENY)
45         {
46                 results.push_back(sn+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
47                 this->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
48                         (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
49                 return;
50         }
51
52         switch (statschar)
53         {
54                 /* stats p (show listening ports) */
55                 case 'p':
56                 {
57                         for (size_t i = 0; i < this->ports.size(); i++)
58                         {
59                                 std::string ip = this->ports[i]->bind_addr;
60                                 if (ip.empty())
61                                         ip.assign("*");
62                                 std::string type = ports[i]->bind_tag->getString("type", "clients");
63                                 std::string hook = ports[i]->bind_tag->getString("ssl", "plaintext");
64
65                                 results.push_back(sn+" 249 "+user->nick+" :"+ ip + ":"+ConvToStr(ports[i]->bind_port)+
66                                         " (" + type + ", " + hook + ")");
67                         }
68                 }
69                 break;
70
71                 /* These stats symbols must be handled by a linking module */
72                 case 'n':
73                 case 'c':
74                 break;
75
76                 case 'i':
77                 {
78                         for (ClassVector::iterator i = this->Config->Classes.begin(); i != this->Config->Classes.end(); i++)
79                         {
80                                 ConnectClass* c = *i;
81                                 std::stringstream res;
82                                 res << sn << " 215 " << user->nick << " I " << c->name << ' ';
83                                 if (c->type == CC_ALLOW)
84                                         res << '+';
85                                 if (c->type == CC_DENY)
86                                         res << '-';
87
88                                 if (c->type == CC_NAMED)
89                                         res << '*';
90                                 else
91                                         res << c->host;
92
93                                 res << ' ' << c->config->getString("port", "*") << ' ';
94
95                                 res << c->GetRecvqMax() << ' ' << c->GetSendqSoftMax() << ' ' << c->GetSendqHardMax()
96                                         << ' ' << c->GetCommandRate() << ' ' << c->GetPenaltyThreshold();
97                                 if (c->fakelag)
98                                         res << '*';
99                                 results.push_back(res.str());
100                         }
101                 }
102                 break;
103
104                 case 'Y':
105                 {
106                         int idx = 0;
107                         for (ClassVector::iterator i = this->Config->Classes.begin(); i != this->Config->Classes.end(); i++)
108                         {
109                                 ConnectClass* c = *i;
110                                 results.push_back(sn+" 215 "+user->nick+" i NOMATCH * "+c->GetHost()+" "+ConvToStr(c->limit ? c->limit : this->SE->GetMaxFds())+" "+ConvToStr(idx)+" "+this->Config->ServerName+" *");
111                                 results.push_back(sn+" 218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(c->GetPingTime())+" 0 "+ConvToStr(c->GetSendqHardMax())+" :"+
112                                                 ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout()));
113                                 idx++;
114                         }
115                 }
116                 break;
117
118                 case 'U':
119                 {
120                         for(std::map<irc::string, bool>::iterator i = Config->ulines.begin(); i != Config->ulines.end(); ++i)
121                         {
122                                 results.push_back(sn+" 248 "+user->nick+" U "+std::string(i->first.c_str()));
123                         }
124                 }
125                 break;
126
127                 case 'P':
128                 {
129                         int idx = 0;
130                         for (user_hash::iterator i = this->Users->clientlist->begin(); i != this->Users->clientlist->end(); i++)
131                         {
132                                 if (IS_OPER(i->second) && !this->ULine(i->second->server))
133                                 {
134                                         results.push_back(sn+" 249 "+user->nick+" :"+i->second->nick+" ("+i->second->ident+"@"+i->second->dhost+") Idle: "+
135                                                         (IS_LOCAL(i->second) ? ConvToStr(this->Time() - i->second->idle_lastmsg) + " secs" : "unavailable"));
136                                         idx++;
137                                 }
138                         }
139                         results.push_back(sn+" 249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)");
140                 }
141                 break;
142
143                 case 'k':
144                         this->XLines->InvokeStats("K",216,user,results);
145                 break;
146                 case 'g':
147                         this->XLines->InvokeStats("G",223,user,results);
148                 break;
149                 case 'q':
150                         this->XLines->InvokeStats("Q",217,user,results);
151                 break;
152                 case 'Z':
153                         this->XLines->InvokeStats("Z",223,user,results);
154                 break;
155                 case 'e':
156                         this->XLines->InvokeStats("E",223,user,results);
157                 break;
158                 case 'E':
159                         results.push_back(sn+" 249 "+user->nick+" :Total events: "+ConvToStr(this->SE->TotalEvents));
160                         results.push_back(sn+" 249 "+user->nick+" :Read events:  "+ConvToStr(this->SE->ReadEvents));
161                         results.push_back(sn+" 249 "+user->nick+" :Write events: "+ConvToStr(this->SE->WriteEvents));
162                         results.push_back(sn+" 249 "+user->nick+" :Error events: "+ConvToStr(this->SE->ErrorEvents));
163                 break;
164
165                 /* stats m (list number of times each command has been used, plus bytecount) */
166                 case 'm':
167                         for (Commandtable::iterator i = this->Parser->cmdlist.begin(); i != this->Parser->cmdlist.end(); i++)
168                         {
169                                 if (i->second->use_count)
170                                 {
171                                         /* RPL_STATSCOMMANDS */
172                                         results.push_back(sn+" 212 "+user->nick+" "+i->second->name+" "+ConvToStr(i->second->use_count)+" "+ConvToStr(i->second->total_bytes));
173                                 }
174                         }
175                 break;
176
177                 /* stats z (debug and memory info) */
178                 case 'z':
179                 {
180                         results.push_back(sn+" 249 "+user->nick+" :Users: "+ConvToStr(this->Users->clientlist->size()));
181                         results.push_back(sn+" 249 "+user->nick+" :Channels: "+ConvToStr(this->chanlist->size()));
182                         results.push_back(sn+" 249 "+user->nick+" :Commands: "+ConvToStr(this->Parser->cmdlist.size()));
183
184                         if (!this->Config->WhoWasGroupSize == 0 && !this->Config->WhoWasMaxGroups == 0)
185                         {
186                                 Module* whowas = Modules->Find("cmd_whowas.so");
187                                 if (whowas)
188                                 {
189                                         WhowasRequest req(NULL, whowas, WhowasRequest::WHOWAS_STATS);
190                                         req.user = user;
191                                         req.Send();
192                                         results.push_back(sn+" 249 "+user->nick+" :"+req.value);
193                                 }
194                         }
195
196                         float kbitpersec_in, kbitpersec_out, kbitpersec_total;
197                         char kbitpersec_in_s[30], kbitpersec_out_s[30], kbitpersec_total_s[30];
198
199                         this->SE->GetStats(kbitpersec_in, kbitpersec_out, kbitpersec_total);
200
201                         snprintf(kbitpersec_total_s, 30, "%03.5f", kbitpersec_total);
202                         snprintf(kbitpersec_out_s, 30, "%03.5f", kbitpersec_out);
203                         snprintf(kbitpersec_in_s, 30, "%03.5f", kbitpersec_in);
204
205                         results.push_back(sn+" 249 "+user->nick+" :Bandwidth total:  "+ConvToStr(kbitpersec_total_s)+" kilobits/sec");
206                         results.push_back(sn+" 249 "+user->nick+" :Bandwidth out:    "+ConvToStr(kbitpersec_out_s)+" kilobits/sec");
207                         results.push_back(sn+" 249 "+user->nick+" :Bandwidth in:     "+ConvToStr(kbitpersec_in_s)+" kilobits/sec");
208
209 #ifndef WIN32
210                         /* Moved this down here so all the not-windows stuff (look w00tie, I didn't say win32!) is in one ifndef.
211                          * Also cuts out some identical code in both branches of the ifndef. -- Om
212                          */
213                         rusage R;
214
215                         /* Not sure why we were doing '0' with a RUSAGE_SELF comment rather than just using RUSAGE_SELF -- Om */
216                         if (!getrusage(RUSAGE_SELF,&R)) /* RUSAGE_SELF */
217                         {
218                                 results.push_back(sn+" 249 "+user->nick+" :Total allocation: "+ConvToStr(R.ru_maxrss)+"K");
219                                 results.push_back(sn+" 249 "+user->nick+" :Signals:          "+ConvToStr(R.ru_nsignals));
220                                 results.push_back(sn+" 249 "+user->nick+" :Page faults:      "+ConvToStr(R.ru_majflt));
221                                 results.push_back(sn+" 249 "+user->nick+" :Swaps:            "+ConvToStr(R.ru_nswap));
222                                 results.push_back(sn+" 249 "+user->nick+" :Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw));
223
224                                 char percent[30];
225
226                                 float n_elapsed = (ServerInstance->Time() - this->stats->LastSampled.tv_sec) * 1000000
227                                         + (ServerInstance->Time_ns() - this->stats->LastSampled.tv_nsec) / 1000;
228                                 float n_eaten = ((R.ru_utime.tv_sec - this->stats->LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - this->stats->LastCPU.tv_usec);
229                                 float per = (n_eaten / n_elapsed) * 100;
230
231                                 snprintf(percent, 30, "%03.5f%%", per);
232                                 results.push_back(sn+" 249 "+user->nick+" :CPU Use (now):    "+percent);
233
234                                 n_elapsed = ServerInstance->Time() - ServerInstance->startup_time;
235                                 n_eaten = (float)R.ru_utime.tv_sec + R.ru_utime.tv_usec / 100000.0;
236                                 per = (n_eaten / n_elapsed) * 100;
237                                 snprintf(percent, 30, "%03.5f%%", per);
238                                 results.push_back(sn+" 249 "+user->nick+" :CPU Use (total):  "+percent);
239                         }
240 #else
241                         PROCESS_MEMORY_COUNTERS MemCounters;
242                         if (GetProcessMemoryInfo(GetCurrentProcess(), &MemCounters, sizeof(MemCounters)))
243                         {
244                                 results.push_back(sn+" 249 "+user->nick+" :Total allocation: "+ConvToStr((MemCounters.WorkingSetSize + MemCounters.PagefileUsage) / 1024)+"K");
245                                 results.push_back(sn+" 249 "+user->nick+" :Pagefile usage:   "+ConvToStr(MemCounters.PagefileUsage / 1024)+"K");
246                                 results.push_back(sn+" 249 "+user->nick+" :Page faults:      "+ConvToStr(MemCounters.PageFaultCount));
247                                 results.push_back(sn+" 249 "+user->nick+" :CPU Usage: " + ConvToStr(getcpu()) + "%");
248                         }
249 #endif
250                 }
251                 break;
252
253                 case 'T':
254                 {
255                         char buffer[MAXBUF];
256                         results.push_back(sn+" 249 "+user->nick+" :accepts "+ConvToStr(this->stats->statsAccept)+" refused "+ConvToStr(this->stats->statsRefused));
257                         results.push_back(sn+" 249 "+user->nick+" :unknown commands "+ConvToStr(this->stats->statsUnknown));
258                         results.push_back(sn+" 249 "+user->nick+" :nick collisions "+ConvToStr(this->stats->statsCollisions));
259                         results.push_back(sn+" 249 "+user->nick+" :dns requests "+ConvToStr(this->stats->statsDnsGood+this->stats->statsDnsBad)+" succeeded "+ConvToStr(this->stats->statsDnsGood)+" failed "+ConvToStr(this->stats->statsDnsBad));
260                         results.push_back(sn+" 249 "+user->nick+" :connection count "+ConvToStr(this->stats->statsConnects));
261                         snprintf(buffer,MAXBUF," 249 %s :bytes sent %5.2fK recv %5.2fK",
262                                 user->nick.c_str(),this->stats->statsSent / 1024.0,this->stats->statsRecv / 1024.0);
263                         results.push_back(sn+buffer);
264                 }
265                 break;
266
267                 /* stats o */
268                 case 'o':
269                 {
270                         ConfigTagList tags = ServerInstance->Config->ConfTags("oper");
271                         for(ConfigIter i = tags.first; i != tags.second; ++i)
272                         {
273                                 ConfigTag* tag = i->second;
274                                 results.push_back(sn+" 243 "+user->nick+" O "+tag->getString("host")+" * "+
275                                         tag->getString("name") + " " + tag->getString("type")+" 0");
276                         }
277                 }
278                 break;
279                 case 'O':
280                 {
281                         for(OperIndex::iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); i++)
282                         {
283                                 // just the types, not the actual oper blocks...
284                                 if (i->first[0] != ' ')
285                                         continue;
286                                 OperInfo* tag = i->second;
287                                 tag->init();
288                                 std::string umodes;
289                                 std::string cmodes;
290                                 for(char c='A'; c < 'z'; c++)
291                                 {
292                                         ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER);
293                                         if (mh && mh->NeedsOper() && tag->AllowedUserModes[c - 'A'])
294                                                 umodes.push_back(c);
295                                         mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL);
296                                         if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A'])
297                                                 cmodes.push_back(c);
298                                 }
299                                 results.push_back(sn+" 243 "+user->nick+" O "+tag->NameStr() + " " + umodes + " " + cmodes);
300                         }
301                 }
302                 break;
303
304                 /* stats l (show user I/O stats) */
305                 case 'l':
306                         results.push_back(sn+" 211 "+user->nick+" :nick[ident@host] sendq cmds_out bytes_out cmds_in bytes_in time_open");
307                         for (std::vector<LocalUser*>::iterator n = this->Users->local_users.begin(); n != this->Users->local_users.end(); n++)
308                         {
309                                 LocalUser* i = *n;
310                                 results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->dhost+"] "+ConvToStr(i->eh.getSendQSize())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(this->Time() - i->age));
311                         }
312                 break;
313
314                 /* stats L (show user I/O stats with IP addresses) */
315                 case 'L':
316                         results.push_back(sn+" 211 "+user->nick+" :nick[ident@ip] sendq cmds_out bytes_out cmds_in bytes_in time_open");
317                         for (std::vector<LocalUser*>::iterator n = this->Users->local_users.begin(); n != this->Users->local_users.end(); n++)
318                         {
319                                 LocalUser* i = *n;
320                                 results.push_back(sn+" 211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->GetIPString()+"] "+ConvToStr(i->eh.getSendQSize())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(this->Time() - i->age));
321                         }
322                 break;
323
324                 /* stats u (show server uptime) */
325                 case 'u':
326                 {
327                         time_t current_time = 0;
328                         current_time = this->Time();
329                         time_t server_uptime = current_time - this->startup_time;
330                         struct tm* stime;
331                         stime = gmtime(&server_uptime);
332                         /* i dont know who the hell would have an ircd running for over a year nonstop, but
333                          * Craig suggested this, and it seemed a good idea so in it went */
334                         if (stime->tm_year > 70)
335                         {
336                                 char buffer[MAXBUF];
337                                 snprintf(buffer,MAXBUF," 242 %s :Server up %d years, %d days, %.2d:%.2d:%.2d",user->nick.c_str(),(stime->tm_year-70),stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec);
338                                 results.push_back(sn+buffer);
339                         }
340                         else
341                         {
342                                 char buffer[MAXBUF];
343                                 snprintf(buffer,MAXBUF," 242 %s :Server up %d days, %.2d:%.2d:%.2d",user->nick.c_str(),stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec);
344                                 results.push_back(sn+buffer);
345                         }
346                 }
347                 break;
348
349                 default:
350                 break;
351         }
352
353         results.push_back(sn+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
354         this->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",
355                 (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str());
356         return;
357 }