1 |
michael |
3389 |
/* Statistics generation (StatServ) main module. |
2 |
|
|
* Based on code by Andrew Kempe (TheShadow). |
3 |
|
|
* |
4 |
|
|
* IRC Services is copyright (c) 1996-2009 Andrew Church. |
5 |
|
|
* E-mail: <achurch@achurch.org> |
6 |
|
|
* Parts written by Andrew Kempe and others. |
7 |
|
|
* This program is free but copyrighted software; see the file GPL.txt for |
8 |
|
|
* details. |
9 |
|
|
*/ |
10 |
|
|
|
11 |
|
|
#include "services.h" |
12 |
|
|
#include "modules.h" |
13 |
|
|
#include "conffile.h" |
14 |
|
|
#include "commands.h" |
15 |
|
|
#include "databases.h" |
16 |
|
|
#include "language.h" |
17 |
|
|
#include "modules/operserv/operserv.h" |
18 |
|
|
|
19 |
|
|
#include "statserv.h" |
20 |
|
|
|
21 |
|
|
/*************************************************************************/ |
22 |
|
|
|
23 |
|
|
static Module *module_operserv; |
24 |
|
|
static Module *module_nickserv; |
25 |
|
|
|
26 |
|
|
static int cb_command = -1; |
27 |
|
|
static int cb_help = -1; |
28 |
|
|
static int cb_help_cmds = -1; |
29 |
|
|
|
30 |
|
|
|
31 |
|
|
char *s_StatServ; |
32 |
|
|
static char *desc_StatServ; |
33 |
|
|
static int SSOpersOnly; |
34 |
|
|
|
35 |
|
|
static int16 servercnt = 0; /* Number of online servers */ |
36 |
|
|
|
37 |
|
|
/*************************************************************************/ |
38 |
|
|
|
39 |
|
|
static void do_help(User *u); |
40 |
|
|
static void do_servers(User *u); |
41 |
|
|
static void do_users(User *u); |
42 |
|
|
|
43 |
|
|
/*************************************************************************/ |
44 |
|
|
|
45 |
|
|
static Command cmds[] = { |
46 |
|
|
{ "HELP", do_help, NULL, -1, -1,-1 }, |
47 |
|
|
{ "SERVERS", do_servers, NULL, -1, STAT_HELP_SERVERS, |
48 |
|
|
STAT_OPER_HELP_SERVERS }, |
49 |
|
|
{ "USERS", do_users, NULL, STAT_HELP_USERS, -1,-1 }, |
50 |
|
|
{ NULL } |
51 |
|
|
}; |
52 |
|
|
|
53 |
|
|
/*************************************************************************/ |
54 |
|
|
/**************************** Database stuff *****************************/ |
55 |
|
|
/*************************************************************************/ |
56 |
|
|
|
57 |
|
|
#define HASHFUNC(key) DEFAULT_HASHFUNC(key) |
58 |
|
|
#define EXPIRE_CHECK(node) 0 |
59 |
|
|
#define add_serverstats static _add_serverstats |
60 |
|
|
#define del_serverstats static _del_serverstats |
61 |
|
|
#include "hash.h" |
62 |
|
|
DEFINE_HASH(serverstats, ServerStats, name); |
63 |
|
|
#undef add_serverstats |
64 |
|
|
#undef del_serverstats |
65 |
|
|
|
66 |
|
|
static void *alloc_serverstats(void) |
67 |
|
|
{ |
68 |
|
|
return scalloc(sizeof(ServerStats), 1); |
69 |
|
|
} |
70 |
|
|
|
71 |
|
|
ServerStats *add_serverstats(ServerStats *ss) |
72 |
|
|
{ |
73 |
|
|
_add_serverstats(ss); |
74 |
|
|
return ss; |
75 |
|
|
} |
76 |
|
|
|
77 |
|
|
void del_serverstats(ServerStats *ss) |
78 |
|
|
{ |
79 |
|
|
_del_serverstats(ss); |
80 |
|
|
free_serverstats(ss); |
81 |
|
|
} |
82 |
|
|
|
83 |
|
|
ServerStats *put_serverstats(ServerStats *ss) |
84 |
|
|
{ |
85 |
|
|
return ss; |
86 |
|
|
} |
87 |
|
|
|
88 |
|
|
EXPORT_FUNC(add_serverstats) |
89 |
|
|
EXPORT_FUNC(del_serverstats) |
90 |
|
|
EXPORT_FUNC(get_serverstats) |
91 |
|
|
EXPORT_FUNC(put_serverstats) |
92 |
|
|
EXPORT_FUNC(first_serverstats) |
93 |
|
|
EXPORT_FUNC(next_serverstats) |
94 |
|
|
|
95 |
|
|
/*************************************************************************/ |
96 |
|
|
|
97 |
|
|
/* Free all memory used by database tables. */ |
98 |
|
|
|
99 |
|
|
static void clean_dbtables(void) |
100 |
|
|
{ |
101 |
|
|
ServerStats *ss; |
102 |
|
|
for (ss = first_serverstats(); ss; ss = next_serverstats()) |
103 |
|
|
free_serverstats(ss); |
104 |
|
|
} |
105 |
|
|
|
106 |
|
|
/*************************************************************************/ |
107 |
|
|
|
108 |
|
|
/* Database load helper: if the server was online when the databas was last |
109 |
|
|
* saved, set t_quit to a time just before Services started up, so servers |
110 |
|
|
* are never seen as online until we actually receive the SERVER message. |
111 |
|
|
*/ |
112 |
|
|
static void db_put_t_quit(void *record, const void *value) |
113 |
|
|
{ |
114 |
|
|
ServerStats *ss = (ServerStats *)record; |
115 |
|
|
if (SS_IS_ONLINE(ss)) { |
116 |
|
|
ss->t_quit = time(NULL) - 1; |
117 |
|
|
if (SS_IS_ONLINE(ss)) { /* Just in case */ |
118 |
|
|
ss->t_quit = ss->t_join + 1; |
119 |
|
|
} |
120 |
|
|
} |
121 |
|
|
} |
122 |
|
|
|
123 |
|
|
static DBField stat_servers_dbfields[] = { |
124 |
|
|
{ "name", DBTYPE_STRING, offsetof(ServerStats,name) }, |
125 |
|
|
{ "t_join", DBTYPE_TIME, offsetof(ServerStats,t_join) }, |
126 |
|
|
{ "t_quit", DBTYPE_TIME, offsetof(ServerStats,t_quit), |
127 |
|
|
.put = db_put_t_quit}, |
128 |
|
|
{ "quit_message", DBTYPE_STRING, offsetof(ServerStats,quit_message) }, |
129 |
|
|
{ NULL } |
130 |
|
|
}; |
131 |
|
|
static DBTable stat_servers_dbtable = { |
132 |
|
|
.name = "stat-servers", |
133 |
|
|
.newrec = alloc_serverstats, |
134 |
|
|
.freerec = (void *)free_serverstats, |
135 |
|
|
.insert = (void *)add_serverstats, |
136 |
|
|
.first = (void *)first_serverstats, |
137 |
|
|
.next = (void *)next_serverstats, |
138 |
|
|
.fields = stat_servers_dbfields, |
139 |
|
|
}; |
140 |
|
|
|
141 |
|
|
/*************************************************************************/ |
142 |
|
|
/****************************** Statistics *******************************/ |
143 |
|
|
/*************************************************************************/ |
144 |
|
|
|
145 |
|
|
/* Introduce the StatServ pseudoclient. */ |
146 |
|
|
|
147 |
|
|
static int introduce_statserv(const char *nick) |
148 |
|
|
{ |
149 |
|
|
if (!nick || irc_stricmp(nick, s_StatServ) == 0) { |
150 |
|
|
send_pseudo_nick(s_StatServ, desc_StatServ, PSEUDO_INVIS); |
151 |
|
|
if (nick) |
152 |
|
|
return 1; |
153 |
|
|
} |
154 |
|
|
return 0; |
155 |
|
|
} |
156 |
|
|
|
157 |
|
|
/*************************************************************************/ |
158 |
|
|
|
159 |
|
|
/* Main StatServ routine. */ |
160 |
|
|
|
161 |
|
|
static int statserv(const char *source, const char *target, char *buf) |
162 |
|
|
{ |
163 |
|
|
const char *cmd; |
164 |
|
|
const char *s; |
165 |
|
|
User *u; |
166 |
|
|
|
167 |
|
|
if (irc_stricmp(target, s_StatServ) != 0) |
168 |
|
|
return 0; |
169 |
|
|
|
170 |
|
|
u = get_user(source); |
171 |
|
|
if (!u) { |
172 |
|
|
module_log("user record for %s not found", source); |
173 |
|
|
notice(s_StatServ, source, getstring(NULL,INTERNAL_ERROR)); |
174 |
|
|
return 1; |
175 |
|
|
} |
176 |
|
|
|
177 |
|
|
if (SSOpersOnly && !is_oper(u)) { |
178 |
|
|
notice_lang(s_StatServ, u, ACCESS_DENIED); |
179 |
|
|
return 1; |
180 |
|
|
} |
181 |
|
|
|
182 |
|
|
cmd = strtok(buf, " "); |
183 |
|
|
if (!cmd) { |
184 |
|
|
return 1; |
185 |
|
|
} else if (stricmp(cmd, "\1PING") == 0) { |
186 |
|
|
if (!(s = strtok_remaining())) |
187 |
|
|
s = "\1"; |
188 |
|
|
notice(s_StatServ, source, "\1PING %s", s); |
189 |
|
|
} else { |
190 |
|
|
if (call_callback_2(cb_command, u, cmd) <= 0) |
191 |
|
|
run_cmd(s_StatServ, u, THIS_MODULE, cmd); |
192 |
|
|
} |
193 |
|
|
return 1; |
194 |
|
|
} |
195 |
|
|
|
196 |
|
|
/*************************************************************************/ |
197 |
|
|
|
198 |
|
|
/* Return a /WHOIS response for StatServ. */ |
199 |
|
|
|
200 |
|
|
static int statserv_whois(const char *source, char *who, char *extra) |
201 |
|
|
{ |
202 |
|
|
if (irc_stricmp(who, s_StatServ) != 0) |
203 |
|
|
return 0; |
204 |
|
|
send_cmd(ServerName, "311 %s %s %s %s * :%s", source, who, |
205 |
|
|
ServiceUser, ServiceHost, desc_StatServ); |
206 |
|
|
send_cmd(ServerName, "312 %s %s %s :%s", source, who, |
207 |
|
|
ServerName, ServerDesc); |
208 |
|
|
send_cmd(ServerName, "313 %s %s :is a network service", source, who); |
209 |
|
|
send_cmd(ServerName, "318 %s %s End of /WHOIS response.", source, who); |
210 |
|
|
return 1; |
211 |
|
|
} |
212 |
|
|
|
213 |
|
|
/*************************************************************************/ |
214 |
|
|
|
215 |
|
|
/* Callback for NickServ REGISTER/LINK check; we disallow |
216 |
|
|
* registration/linking of the StatServ pseudoclient nickname. |
217 |
|
|
*/ |
218 |
|
|
|
219 |
|
|
static int do_reglink_check(const User *u, const char *nick, |
220 |
|
|
const char *pass, const char *email) |
221 |
|
|
{ |
222 |
|
|
return irc_stricmp(nick, s_StatServ) == 0; |
223 |
|
|
} |
224 |
|
|
|
225 |
|
|
/*************************************************************************/ |
226 |
|
|
/************************* Server info display ***************************/ |
227 |
|
|
/*************************************************************************/ |
228 |
|
|
|
229 |
|
|
/* Return a help message. */ |
230 |
|
|
|
231 |
|
|
static void do_help(User *u) |
232 |
|
|
{ |
233 |
|
|
char *cmd = strtok_remaining(); |
234 |
|
|
|
235 |
|
|
if (!cmd) { |
236 |
|
|
notice_help(s_StatServ, u, STAT_HELP); |
237 |
|
|
} else if (stricmp(cmd, "COMMANDS") == 0) { |
238 |
|
|
notice_help(s_StatServ, u, STAT_HELP_COMMANDS); |
239 |
|
|
call_callback_2(cb_help_cmds, u, 0); |
240 |
|
|
} else if (call_callback_2(cb_help, u, cmd) > 0) { |
241 |
|
|
return; |
242 |
|
|
} else { |
243 |
|
|
help_cmd(s_StatServ, u, THIS_MODULE, cmd); |
244 |
|
|
} |
245 |
|
|
} |
246 |
|
|
|
247 |
|
|
/*************************************************************************/ |
248 |
|
|
|
249 |
|
|
static void do_servers(User *u) |
250 |
|
|
{ |
251 |
|
|
ServerStats *ss = NULL; |
252 |
|
|
const char *cmd = strtok(NULL, " "); |
253 |
|
|
char *mask = strtok(NULL, " "); |
254 |
|
|
int count = 0, nservers = 0; |
255 |
|
|
|
256 |
|
|
if (!cmd) |
257 |
|
|
cmd = ""; |
258 |
|
|
|
259 |
|
|
if (stricmp(cmd, "STATS") == 0) { |
260 |
|
|
ServerStats *ss_lastquit = NULL; |
261 |
|
|
int onlinecount = 0; |
262 |
|
|
char lastquit_buf[BUFSIZE]; |
263 |
|
|
|
264 |
|
|
for (ss = first_serverstats(); ss; ss = next_serverstats()) { |
265 |
|
|
nservers++; |
266 |
|
|
if (ss->t_quit > 0 |
267 |
|
|
&& (!ss_lastquit |
268 |
|
|
|| ss->t_quit > ss_lastquit->t_quit)) |
269 |
|
|
ss_lastquit = ss; |
270 |
|
|
if (SS_IS_ONLINE(ss)) |
271 |
|
|
onlinecount++; |
272 |
|
|
} |
273 |
|
|
|
274 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_STATS_TOTAL, nservers); |
275 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_STATS_ON_OFFLINE, |
276 |
|
|
onlinecount, (onlinecount*100)/nservers, |
277 |
|
|
nservers-onlinecount, |
278 |
|
|
((nservers-onlinecount)*100)/nservers); |
279 |
|
|
if (ss_lastquit) { |
280 |
|
|
strftime_lang(lastquit_buf, sizeof(lastquit_buf), u->ngi, |
281 |
|
|
STRFTIME_DATE_TIME_FORMAT, ss_lastquit->t_quit); |
282 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_LASTQUIT_WAS, |
283 |
|
|
ss_lastquit->name, lastquit_buf); |
284 |
|
|
} |
285 |
|
|
|
286 |
|
|
|
287 |
|
|
} else if (stricmp(cmd, "LIST") == 0) { |
288 |
|
|
int matchcount = 0; |
289 |
|
|
|
290 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_LIST_HEADER); |
291 |
|
|
for (ss = first_serverstats(); ss; ss = next_serverstats()) { |
292 |
|
|
if (mask && !match_wild_nocase(mask, ss->name)) |
293 |
|
|
continue; |
294 |
|
|
matchcount++; |
295 |
|
|
if (!SS_IS_ONLINE(ss)) |
296 |
|
|
continue; |
297 |
|
|
count++; |
298 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_LIST_FORMAT, |
299 |
|
|
ss->name, ss->usercnt, |
300 |
|
|
!usercnt ? 0 : (ss->usercnt*100)/usercnt, |
301 |
|
|
ss->opercnt, |
302 |
|
|
!opcnt ? 0 : (ss->opercnt*100)/opcnt); |
303 |
|
|
} |
304 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_LIST_RESULTS, |
305 |
|
|
count, matchcount); |
306 |
|
|
|
307 |
|
|
} else if (stricmp(cmd, "VIEW") == 0) { |
308 |
|
|
char *param = strtok(NULL, " "); |
309 |
|
|
char join_buf[BUFSIZE]; |
310 |
|
|
char quit_buf[BUFSIZE]; |
311 |
|
|
int is_online; |
312 |
|
|
int limitto = 0; /* 0 == none; 1 == online; 2 == offline */ |
313 |
|
|
|
314 |
|
|
if (param) { |
315 |
|
|
if (stricmp(param, "ONLINE") == 0) { |
316 |
|
|
limitto = 1; |
317 |
|
|
} else if (stricmp(param, "OFFLINE") == 0) { |
318 |
|
|
limitto = 2; |
319 |
|
|
} |
320 |
|
|
} |
321 |
|
|
|
322 |
|
|
for (ss = first_serverstats(); ss; ss = next_serverstats()) { |
323 |
|
|
nservers++; |
324 |
|
|
if (mask && !match_wild_nocase(mask, ss->name)) |
325 |
|
|
continue; |
326 |
|
|
is_online = SS_IS_ONLINE(ss); |
327 |
|
|
if (limitto && !((is_online && limitto == 1) || |
328 |
|
|
(!is_online && limitto == 2))) |
329 |
|
|
continue; |
330 |
|
|
|
331 |
|
|
count++; |
332 |
|
|
strftime_lang(join_buf, sizeof(join_buf), u->ngi, |
333 |
|
|
STRFTIME_DATE_TIME_FORMAT, ss->t_join); |
334 |
|
|
if (ss->t_quit != 0) { |
335 |
|
|
strftime_lang(quit_buf, sizeof(quit_buf), u->ngi, |
336 |
|
|
STRFTIME_DATE_TIME_FORMAT, ss->t_quit); |
337 |
|
|
} |
338 |
|
|
|
339 |
|
|
notice_lang(s_StatServ, u, |
340 |
|
|
is_online ? STAT_SERVERS_VIEW_HEADER_ONLINE |
341 |
|
|
: STAT_SERVERS_VIEW_HEADER_OFFLINE, |
342 |
|
|
ss->name); |
343 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_VIEW_LASTJOIN, join_buf); |
344 |
|
|
if (ss->t_quit > 0) |
345 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_VIEW_LASTQUIT, |
346 |
|
|
quit_buf); |
347 |
|
|
if (ss->quit_message) |
348 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_VIEW_QUITMSG, |
349 |
|
|
ss->quit_message); |
350 |
|
|
if (is_online) |
351 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_VIEW_USERS_OPERS, |
352 |
|
|
ss->usercnt, |
353 |
|
|
!usercnt ? 0 : (ss->usercnt*100)/usercnt, |
354 |
|
|
ss->opercnt, |
355 |
|
|
!opcnt ? 0 : (ss->opercnt*100)/opcnt); |
356 |
|
|
} |
357 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_VIEW_RESULTS, count, nservers); |
358 |
|
|
|
359 |
|
|
} else if (!is_services_admin(u)) { |
360 |
|
|
if (is_oper(u)) |
361 |
|
|
notice_lang(s_StatServ, u, PERMISSION_DENIED); |
362 |
|
|
else |
363 |
|
|
syntax_error(s_StatServ, u, "SERVERS", STAT_SERVERS_SYNTAX); |
364 |
|
|
|
365 |
|
|
/* Only Services admins have access from here on! */ |
366 |
|
|
|
367 |
|
|
} else if (stricmp(cmd, "DELETE") == 0) { |
368 |
|
|
if (!mask) { |
369 |
|
|
syntax_error(s_StatServ, u, "SERVERS", STAT_SERVERS_DELETE_SYNTAX); |
370 |
|
|
} else if (!(ss = get_serverstats(mask))) { |
371 |
|
|
notice_lang(s_StatServ, u, SERV_X_NOT_FOUND, mask); |
372 |
|
|
} else if (SS_IS_ONLINE(ss)) { |
373 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_REMOVE_SERV_FIRST, mask); |
374 |
|
|
} else { |
375 |
|
|
del_serverstats(ss); |
376 |
|
|
ss = NULL; |
377 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_DELETE_DONE, mask); |
378 |
|
|
} |
379 |
|
|
|
380 |
|
|
} else if (stricmp(cmd, "COPY") == 0) { |
381 |
|
|
const char *newname = strtok(NULL, " "); |
382 |
|
|
ServerStats *newss; |
383 |
|
|
if (!mask || !newname) { |
384 |
|
|
syntax_error(s_StatServ, u, "SERVERS", STAT_SERVERS_COPY_SYNTAX); |
385 |
|
|
} else if (!(ss = get_serverstats(mask))) { |
386 |
|
|
notice_lang(s_StatServ, u, SERV_X_NOT_FOUND, mask); |
387 |
|
|
} else if ((newss = get_serverstats(newname)) != NULL) { |
388 |
|
|
put_serverstats(newss); |
389 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_SERVER_EXISTS, newname); |
390 |
|
|
} else { |
391 |
|
|
newss = new_serverstats(newname); |
392 |
|
|
newss->t_join = ss->t_join; |
393 |
|
|
newss->t_quit = ss->t_quit; |
394 |
|
|
if (ss->quit_message) { |
395 |
|
|
newss->quit_message = sstrdup(ss->quit_message); |
396 |
|
|
} |
397 |
|
|
add_serverstats(newss); |
398 |
|
|
put_serverstats(newss); |
399 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_COPY_DONE, mask, newname); |
400 |
|
|
} |
401 |
|
|
|
402 |
|
|
} else if (stricmp(cmd, "RENAME") == 0) { |
403 |
|
|
const char *newname = strtok(NULL, " "); |
404 |
|
|
ServerStats *newss; |
405 |
|
|
if (!mask || !newname) { |
406 |
|
|
syntax_error(s_StatServ, u, "SERVERS", STAT_SERVERS_RENAME_SYNTAX); |
407 |
|
|
} else if (!(ss = get_serverstats(mask))) { |
408 |
|
|
notice_lang(s_StatServ, u, SERV_X_NOT_FOUND, mask); |
409 |
|
|
} else if ((newss = get_serverstats(newname)) != NULL) { |
410 |
|
|
put_serverstats(newss); |
411 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_SERVER_EXISTS, newname); |
412 |
|
|
} else if (SS_IS_ONLINE(ss)) { |
413 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_REMOVE_SERV_FIRST, mask); |
414 |
|
|
} else { |
415 |
|
|
newss = new_serverstats(newname); |
416 |
|
|
newss->t_join = ss->t_join; |
417 |
|
|
newss->t_quit = ss->t_quit; |
418 |
|
|
if (ss->quit_message) { |
419 |
|
|
newss->quit_message = sstrdup(ss->quit_message); |
420 |
|
|
} |
421 |
|
|
del_serverstats(ss); |
422 |
|
|
ss = NULL; |
423 |
|
|
add_serverstats(newss); |
424 |
|
|
put_serverstats(newss); |
425 |
|
|
notice_lang(s_StatServ, u, STAT_SERVERS_RENAME_DONE, |
426 |
|
|
mask, newname); |
427 |
|
|
} |
428 |
|
|
|
429 |
|
|
} else { |
430 |
|
|
syntax_error(s_StatServ, u, "SERVERS", STAT_SERVERS_SYNTAX); |
431 |
|
|
} |
432 |
|
|
|
433 |
|
|
put_serverstats(ss); |
434 |
|
|
} |
435 |
|
|
|
436 |
|
|
/*************************************************************************/ |
437 |
|
|
|
438 |
|
|
static void do_users(User *u) |
439 |
|
|
{ |
440 |
|
|
const char *cmd = strtok(NULL, " "); |
441 |
|
|
int avgusers, avgopers; |
442 |
|
|
|
443 |
|
|
if (!cmd) |
444 |
|
|
cmd = ""; |
445 |
|
|
|
446 |
|
|
if (stricmp(cmd, "STATS") == 0) { |
447 |
|
|
notice_lang(s_StatServ, u, STAT_USERS_TOTUSERS, usercnt); |
448 |
|
|
notice_lang(s_StatServ, u, STAT_USERS_TOTOPERS, opcnt); |
449 |
|
|
avgusers = (usercnt + servercnt/2) / servercnt; |
450 |
|
|
avgopers = (opcnt*10 + servercnt/2) / servercnt; |
451 |
|
|
notice_lang(s_StatServ, u, STAT_USERS_SERVUSERS, avgusers); |
452 |
|
|
notice_lang(s_StatServ, u, STAT_USERS_SERVOPERS, |
453 |
|
|
avgopers/10, avgopers%10); |
454 |
|
|
} else { |
455 |
|
|
syntax_error(s_StatServ, u, "USERS", STAT_USERS_SYNTAX); |
456 |
|
|
} |
457 |
|
|
} |
458 |
|
|
|
459 |
|
|
/*************************************************************************/ |
460 |
|
|
/******************** ServerStats new/free (global) **********************/ |
461 |
|
|
/*************************************************************************/ |
462 |
|
|
|
463 |
|
|
/* Create a new ServerStats structure for the given server name and return |
464 |
|
|
* it. Always successful. |
465 |
|
|
*/ |
466 |
|
|
|
467 |
|
|
EXPORT_FUNC(new_serverstats) |
468 |
|
|
ServerStats *new_serverstats(const char *servername) |
469 |
|
|
{ |
470 |
|
|
ServerStats *ss = alloc_serverstats(); |
471 |
|
|
if (ss) |
472 |
|
|
ss->name = sstrdup(servername); |
473 |
|
|
return ss; |
474 |
|
|
} |
475 |
|
|
|
476 |
|
|
/*************************************************************************/ |
477 |
|
|
|
478 |
|
|
/* Free a ServerStats structure and associated data. */ |
479 |
|
|
|
480 |
|
|
EXPORT_FUNC(free_serverstats) |
481 |
|
|
void free_serverstats(ServerStats *ss) |
482 |
|
|
{ |
483 |
|
|
free(ss->name); |
484 |
|
|
free(ss->quit_message); |
485 |
|
|
free(ss); |
486 |
|
|
} |
487 |
|
|
|
488 |
|
|
/*************************************************************************/ |
489 |
|
|
/************************** Callback routines ****************************/ |
490 |
|
|
/*************************************************************************/ |
491 |
|
|
|
492 |
|
|
/* Handle a server joining. */ |
493 |
|
|
|
494 |
|
|
static int stats_do_server(Server *server) |
495 |
|
|
{ |
496 |
|
|
ServerStats *ss; |
497 |
|
|
|
498 |
|
|
servercnt++; |
499 |
|
|
|
500 |
|
|
ss = get_serverstats(server->name); |
501 |
|
|
if (ss) { |
502 |
|
|
/* Server has rejoined us */ |
503 |
|
|
ss->usercnt = 0; |
504 |
|
|
ss->opercnt = 0; |
505 |
|
|
ss->t_join = time(NULL); |
506 |
|
|
} else { |
507 |
|
|
/* Totally new server */ |
508 |
|
|
ss = new_serverstats(server->name); /* cleared to zero */ |
509 |
|
|
ss->t_join = time(NULL); |
510 |
|
|
add_serverstats(ss); |
511 |
|
|
} |
512 |
|
|
|
513 |
|
|
server->stats = ss; |
514 |
|
|
return 0; |
515 |
|
|
} |
516 |
|
|
|
517 |
|
|
/*************************************************************************/ |
518 |
|
|
|
519 |
|
|
/* Handle a server quitting. */ |
520 |
|
|
|
521 |
|
|
static int stats_do_squit(Server *server, const char *quit_message) |
522 |
|
|
{ |
523 |
|
|
ServerStats *ss = server->stats; |
524 |
|
|
|
525 |
|
|
servercnt--; |
526 |
|
|
ss->t_quit = time(NULL); |
527 |
|
|
free(ss->quit_message); |
528 |
|
|
ss->quit_message = *quit_message ? sstrdup(quit_message) : NULL; |
529 |
|
|
put_serverstats(ss); |
530 |
|
|
return 0; |
531 |
|
|
} |
532 |
|
|
|
533 |
|
|
/*************************************************************************/ |
534 |
|
|
|
535 |
|
|
/* Handle a user joining. */ |
536 |
|
|
|
537 |
|
|
static int stats_do_newuser(User *user) |
538 |
|
|
{ |
539 |
|
|
if (user->server) |
540 |
|
|
user->server->stats->usercnt++; |
541 |
|
|
return 0; |
542 |
|
|
} |
543 |
|
|
|
544 |
|
|
/*************************************************************************/ |
545 |
|
|
|
546 |
|
|
/* Handle a user quitting. */ |
547 |
|
|
|
548 |
|
|
static int stats_do_quit(User *user) |
549 |
|
|
{ |
550 |
|
|
if (user->server) { |
551 |
|
|
ServerStats *ss = user->server->stats; |
552 |
|
|
if (!ss) { |
553 |
|
|
module_log("BUG! no serverstats for %s in do_quit(%s)", |
554 |
|
|
user->server->name, user->nick); |
555 |
|
|
return 0; |
556 |
|
|
} |
557 |
|
|
ss->usercnt--; |
558 |
|
|
if (is_oper(user)) |
559 |
|
|
ss->opercnt--; |
560 |
|
|
} |
561 |
|
|
return 0; |
562 |
|
|
} |
563 |
|
|
|
564 |
|
|
/*************************************************************************/ |
565 |
|
|
|
566 |
|
|
/* Handle a user mode change. */ |
567 |
|
|
|
568 |
|
|
static int stats_do_umode(User *user, int modechar, int add) |
569 |
|
|
{ |
570 |
|
|
if (user->server) { |
571 |
|
|
if (modechar == 'o') { |
572 |
|
|
ServerStats *ss = user->server->stats; |
573 |
|
|
if (!ss) { |
574 |
|
|
module_log("BUG! no serverstats for %s in do_quit(%s)", |
575 |
|
|
user->server->name, user->nick); |
576 |
|
|
return 0; |
577 |
|
|
} |
578 |
|
|
if (add) |
579 |
|
|
ss->opercnt++; |
580 |
|
|
else |
581 |
|
|
ss->opercnt--; |
582 |
|
|
} |
583 |
|
|
} |
584 |
|
|
return 0; |
585 |
|
|
} |
586 |
|
|
|
587 |
|
|
/*************************************************************************/ |
588 |
|
|
|
589 |
|
|
/* OperServ STATS ALL callback. */ |
590 |
|
|
|
591 |
|
|
static int do_stats_all(User *user, const char *s_OperServ) |
592 |
|
|
{ |
593 |
|
|
int32 count, mem; |
594 |
|
|
ServerStats *ss; |
595 |
|
|
|
596 |
|
|
count = mem = 0; |
597 |
|
|
for (ss = first_serverstats(); ss; ss = next_serverstats()) { |
598 |
|
|
count++; |
599 |
|
|
mem += sizeof(*ss) + strlen(ss->name)+1; |
600 |
|
|
if (ss->quit_message) |
601 |
|
|
mem += strlen(ss->quit_message)+1; |
602 |
|
|
} |
603 |
|
|
notice_lang(s_OperServ, user, OPER_STATS_ALL_STATSERV_MEM, |
604 |
|
|
count, (mem+512) / 1024); |
605 |
|
|
|
606 |
|
|
return 0; |
607 |
|
|
} |
608 |
|
|
|
609 |
|
|
/*************************************************************************/ |
610 |
|
|
/**************************** Module functions ***************************/ |
611 |
|
|
/*************************************************************************/ |
612 |
|
|
|
613 |
|
|
ConfigDirective module_config[] = { |
614 |
|
|
{ "SSOpersOnly", { { CD_SET, 0, &SSOpersOnly } } }, |
615 |
|
|
{ "StatServName", { { CD_STRING, CF_DIRREQ, &s_StatServ }, |
616 |
|
|
{ CD_STRING, 0, &desc_StatServ } } }, |
617 |
|
|
{ NULL } |
618 |
|
|
}; |
619 |
|
|
|
620 |
|
|
/*************************************************************************/ |
621 |
|
|
|
622 |
|
|
static int do_load_module(Module *mod, const char *modname) |
623 |
|
|
{ |
624 |
|
|
if (strcmp(modname, "operserv/main") == 0) { |
625 |
|
|
module_operserv = mod; |
626 |
|
|
if (!add_callback(mod, "STATS ALL", do_stats_all)) |
627 |
|
|
module_log("Unable to register OperServ STATS ALL callback"); |
628 |
|
|
} |
629 |
|
|
if (strcmp(modname, "nickserv/main") == 0) { |
630 |
|
|
module_nickserv = mod; |
631 |
|
|
if (!add_callback(mod, "REGISTER/LINK check", do_reglink_check)) |
632 |
|
|
module_log("Unable to register NickServ REGISTER/LINK check" |
633 |
|
|
" callback"); |
634 |
|
|
} |
635 |
|
|
return 0; |
636 |
|
|
} |
637 |
|
|
|
638 |
|
|
/*************************************************************************/ |
639 |
|
|
|
640 |
|
|
static int do_unload_module(Module *mod) |
641 |
|
|
{ |
642 |
|
|
if (mod == module_operserv) { |
643 |
|
|
remove_callback(mod, "STATS ALL", do_stats_all); |
644 |
|
|
module_operserv = NULL; |
645 |
|
|
} |
646 |
|
|
if (mod == module_nickserv) { |
647 |
|
|
remove_callback(mod, "REGISTER/LINK check", do_reglink_check); |
648 |
|
|
module_nickserv = NULL; |
649 |
|
|
} |
650 |
|
|
return 0; |
651 |
|
|
} |
652 |
|
|
|
653 |
|
|
/*************************************************************************/ |
654 |
|
|
|
655 |
|
|
static int do_reconfigure(int after_configure) |
656 |
|
|
{ |
657 |
|
|
static char old_s_StatServ[NICKMAX]; |
658 |
|
|
static char *old_desc_StatServ = NULL; |
659 |
|
|
|
660 |
|
|
if (!after_configure) { |
661 |
|
|
/* Before reconfiguration: save old values. */ |
662 |
|
|
strbcpy(old_s_StatServ, s_StatServ); |
663 |
|
|
old_desc_StatServ = strdup(desc_StatServ); |
664 |
|
|
} else { |
665 |
|
|
/* After reconfiguration: handle value changes. */ |
666 |
|
|
if (strcmp(old_s_StatServ, s_StatServ) != 0) |
667 |
|
|
send_nickchange(old_s_StatServ, s_StatServ); |
668 |
|
|
if (!old_desc_StatServ || strcmp(old_desc_StatServ,desc_StatServ) != 0) |
669 |
|
|
send_namechange(s_StatServ, desc_StatServ); |
670 |
|
|
free(old_desc_StatServ); |
671 |
|
|
old_desc_StatServ = NULL; |
672 |
|
|
} /* if (!after_configure) */ |
673 |
|
|
return 0; |
674 |
|
|
} |
675 |
|
|
|
676 |
|
|
/*************************************************************************/ |
677 |
|
|
|
678 |
|
|
int init_module(void) |
679 |
|
|
{ |
680 |
|
|
Module *tmpmod; |
681 |
|
|
|
682 |
|
|
|
683 |
|
|
if (!new_commandlist(THIS_MODULE) |
684 |
|
|
|| !register_commands(THIS_MODULE, cmds) |
685 |
|
|
) { |
686 |
|
|
module_log("Unable to register commands"); |
687 |
|
|
exit_module(0); |
688 |
|
|
return 0; |
689 |
|
|
} |
690 |
|
|
|
691 |
|
|
cb_command = register_callback("command"); |
692 |
|
|
cb_help = register_callback("HELP"); |
693 |
|
|
cb_help_cmds = register_callback("HELP COMMANDS"); |
694 |
|
|
if (cb_command < 0 || cb_help < 0 || cb_help_cmds < 0) { |
695 |
|
|
module_log("Unable to register callbacks"); |
696 |
|
|
exit_module(0); |
697 |
|
|
return 0; |
698 |
|
|
} |
699 |
|
|
|
700 |
|
|
if (!add_callback(NULL, "load module", do_load_module) |
701 |
|
|
|| !add_callback(NULL, "unload module", do_unload_module) |
702 |
|
|
|| !add_callback(NULL, "reconfigure", do_reconfigure) |
703 |
|
|
|| !add_callback(NULL, "introduce_user", introduce_statserv) |
704 |
|
|
|| !add_callback(NULL, "m_privmsg", statserv) |
705 |
|
|
|| !add_callback(NULL, "m_whois", statserv_whois) |
706 |
|
|
|| !add_callback(NULL, "server create", stats_do_server) |
707 |
|
|
|| !add_callback(NULL, "server delete", stats_do_squit) |
708 |
|
|
|| !add_callback(NULL, "user create", stats_do_newuser) |
709 |
|
|
|| !add_callback(NULL, "user delete", stats_do_quit) |
710 |
|
|
|| !add_callback(NULL, "user MODE", stats_do_umode) |
711 |
|
|
) { |
712 |
|
|
module_log("Unable to add callbacks"); |
713 |
|
|
exit_module(0); |
714 |
|
|
return 0; |
715 |
|
|
} |
716 |
|
|
|
717 |
|
|
tmpmod = find_module("nickserv/main"); |
718 |
|
|
if (tmpmod) |
719 |
|
|
do_load_module(tmpmod, "nickserv/main"); |
720 |
|
|
|
721 |
|
|
if (!register_dbtable(&stat_servers_dbtable)) { |
722 |
|
|
module_log("Unable to register database table"); |
723 |
|
|
exit_module(0); |
724 |
|
|
return 0; |
725 |
|
|
} |
726 |
|
|
|
727 |
|
|
if (linked) |
728 |
|
|
introduce_statserv(NULL); |
729 |
|
|
|
730 |
|
|
return 1; |
731 |
|
|
} |
732 |
|
|
|
733 |
|
|
/*************************************************************************/ |
734 |
|
|
|
735 |
|
|
int exit_module(int shutdown_unused) |
736 |
|
|
{ |
737 |
|
|
if (linked) |
738 |
|
|
send_cmd(s_StatServ, "QUIT :"); |
739 |
|
|
|
740 |
|
|
unregister_dbtable(&stat_servers_dbtable); |
741 |
|
|
clean_dbtables(); |
742 |
|
|
|
743 |
|
|
if (module_nickserv) |
744 |
|
|
do_unload_module(module_nickserv); |
745 |
|
|
if (module_operserv) |
746 |
|
|
do_unload_module(module_operserv); |
747 |
|
|
|
748 |
|
|
remove_callback(NULL, "user MODE", stats_do_umode); |
749 |
|
|
remove_callback(NULL, "user delete", stats_do_quit); |
750 |
|
|
remove_callback(NULL, "user create", stats_do_newuser); |
751 |
|
|
remove_callback(NULL, "server delete", stats_do_squit); |
752 |
|
|
remove_callback(NULL, "server create", stats_do_server); |
753 |
|
|
remove_callback(NULL, "m_whois", statserv_whois); |
754 |
|
|
remove_callback(NULL, "m_privmsg", statserv); |
755 |
|
|
remove_callback(NULL, "introduce_user", introduce_statserv); |
756 |
|
|
remove_callback(NULL, "reconfigure", do_reconfigure); |
757 |
|
|
remove_callback(NULL, "unload module", do_unload_module); |
758 |
|
|
remove_callback(NULL, "load module", do_load_module); |
759 |
|
|
|
760 |
|
|
unregister_callback(cb_help_cmds); |
761 |
|
|
unregister_callback(cb_help); |
762 |
|
|
unregister_callback(cb_command); |
763 |
|
|
|
764 |
|
|
unregister_commands(THIS_MODULE, cmds); |
765 |
|
|
del_commandlist(THIS_MODULE); |
766 |
|
|
|
767 |
|
|
return 1; |
768 |
|
|
} |
769 |
|
|
|
770 |
|
|
/*************************************************************************/ |
771 |
|
|
|
772 |
|
|
/* |
773 |
|
|
* Local variables: |
774 |
|
|
* c-file-style: "stroustrup" |
775 |
|
|
* c-file-offsets: ((case-label . *) (statement-case-intro . *)) |
776 |
|
|
* indent-tabs-mode: nil |
777 |
|
|
* End: |
778 |
|
|
* |
779 |
|
|
* vim: expandtab shiftwidth=4: |
780 |
|
|
*/ |