/[cvs]/eggdrop1.8/src/chanprog.c
ViewVC logotype

Contents of /eggdrop1.8/src/chanprog.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.2 - (show annotations) (download) (as text)
Sun Oct 10 18:22:47 2010 UTC (8 years, 7 months ago) by pseudo
Branch: MAIN
Changes since 1.1: +30 -23 lines
File MIME type: text/x-chdr
Added the process and parent pids to .status output. Increased the precision of cpu time reporting.
Added a new Tcl command status to provide access to cpu/memory/cache information now and some more in the future.

1 /*
2 * chanprog.c -- handles:
3 * rmspace()
4 * maintaining the server list
5 * revenge punishment
6 * timers, utimers
7 * telling the current programmed settings
8 * initializing a lot of stuff and loading the tcl scripts
9 *
10 * $Id: chanprog.c,v 1.1.1.1 2010/07/26 21:11:06 simple Exp $
11 */
12 /*
13 * Copyright (C) 1997 Robey Pointer
14 * Copyright (C) 1999 - 2010 Eggheads Development Team
15 *
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 */
30
31 #include "main.h"
32
33 #ifdef HAVE_GETRUSAGE
34 # include <sys/resource.h>
35 # ifdef HAVE_SYS_RUSAGE_H
36 # include <sys/rusage.h>
37 # endif
38 #endif
39
40 #ifdef HAVE_UNAME
41 # include <sys/utsname.h>
42 #endif
43
44 #include "modules.h"
45
46 extern struct userrec *userlist;
47 extern log_t *logs;
48 extern Tcl_Interp *interp;
49 extern char ver[], botnetnick[], firewall[], motdfile[], userfile[], helpdir[],
50 tempdir[], moddir[], notify_new[], owner[], configfile[];
51 extern time_t now, online_since;
52 extern int backgrd, term_z, con_chan, cache_hit, cache_miss, firewallport,
53 default_flags, max_logs, conmask, protect_readonly, make_userfile,
54 noshare, ignore_time, max_socks;
55
56 tcl_timer_t *timer = NULL; /* Minutely timer */
57 tcl_timer_t *utimer = NULL; /* Secondly timer */
58 unsigned long timer_id = 1; /* Next timer of any sort will
59 * have this number */
60 struct chanset_t *chanset = NULL; /* Channel list */
61 char admin[121] = ""; /* Admin info */
62 char origbotname[NICKLEN + 1];
63 char botname[NICKLEN + 1]; /* Primary botname */
64
65
66 /* Remove leading and trailing whitespaces.
67 */
68 void rmspace(char *s)
69 {
70 register char *p = NULL, *q = NULL;
71
72 if (!s || !*s)
73 return;
74
75 /* Remove trailing whitespaces. */
76 for (q = s + strlen(s) - 1; q >= s && egg_isspace(*q); q--);
77 *(q + 1) = 0;
78
79 /* Remove leading whitespaces. */
80 for (p = s; egg_isspace(*p); p++);
81
82 if (p != s)
83 memmove(s, p, q - p + 2);
84 }
85
86
87 /* Returns memberfields if the nick is in the member list.
88 */
89 memberlist *ismember(struct chanset_t *chan, char *nick)
90 {
91 register memberlist *x;
92
93 for (x = chan->channel.member; x && x->nick[0]; x = x->next)
94 if (!rfc_casecmp(x->nick, nick))
95 return x;
96 return NULL;
97 }
98
99 /* Find a chanset by channel name as the server knows it (ie !ABCDEchannel)
100 */
101 struct chanset_t *findchan(const char *name)
102 {
103 register struct chanset_t *chan;
104
105 for (chan = chanset; chan; chan = chan->next)
106 if (!rfc_casecmp(chan->name, name))
107 return chan;
108 return NULL;
109 }
110
111 /* Find a chanset by display name (ie !channel)
112 */
113 struct chanset_t *findchan_by_dname(const char *name)
114 {
115 register struct chanset_t *chan;
116
117 for (chan = chanset; chan; chan = chan->next)
118 if (!rfc_casecmp(chan->dname, name))
119 return chan;
120 return NULL;
121 }
122
123
124 /*
125 * "caching" functions
126 */
127
128 /* Shortcut for get_user_by_host -- might have user record in one
129 * of the channel caches.
130 */
131 struct userrec *check_chanlist(const char *host)
132 {
133 char *nick, *uhost, buf[UHOSTLEN];
134 register memberlist *m;
135 register struct chanset_t *chan;
136
137 strncpyz(buf, host, sizeof buf);
138 uhost = buf;
139 nick = splitnick(&uhost);
140 for (chan = chanset; chan; chan = chan->next)
141 for (m = chan->channel.member; m && m->nick[0]; m = m->next)
142 if (!rfc_casecmp(nick, m->nick) && !egg_strcasecmp(uhost, m->userhost))
143 return m->user;
144 return NULL;
145 }
146
147 /* Shortcut for get_user_by_handle -- might have user record in channels
148 */
149 struct userrec *check_chanlist_hand(const char *hand)
150 {
151 register struct chanset_t *chan;
152 register memberlist *m;
153
154 for (chan = chanset; chan; chan = chan->next)
155 for (m = chan->channel.member; m && m->nick[0]; m = m->next)
156 if (m->user && !egg_strcasecmp(m->user->handle, hand))
157 return m->user;
158 return NULL;
159 }
160
161 /* Clear the user pointers in the chanlists.
162 *
163 * Necessary when a hostmask is added/removed, a user is added or a new
164 * userfile is loaded.
165 */
166 void clear_chanlist(void)
167 {
168 register memberlist *m;
169 register struct chanset_t *chan;
170
171 for (chan = chanset; chan; chan = chan->next)
172 for (m = chan->channel.member; m && m->nick[0]; m = m->next) {
173 m->user = NULL;
174 m->tried_getuser = 0;
175 }
176 }
177
178 /* Clear the user pointer of a specific nick in the chanlists.
179 *
180 * Necessary when a hostmask is added/removed, a nick changes, etc.
181 * Does not completely invalidate the channel cache like clear_chanlist().
182 */
183 void clear_chanlist_member(const char *nick)
184 {
185 register memberlist *m;
186 register struct chanset_t *chan;
187
188 for (chan = chanset; chan; chan = chan->next)
189 for (m = chan->channel.member; m && m->nick[0]; m = m->next)
190 if (!rfc_casecmp(m->nick, nick)) {
191 m->user = NULL;
192 m->tried_getuser = 0;
193 break;
194 }
195 }
196
197 /* If this user@host is in a channel, set it (it was null)
198 */
199 void set_chanlist(const char *host, struct userrec *rec)
200 {
201 char *nick, *uhost, buf[UHOSTLEN];
202 register memberlist *m;
203 register struct chanset_t *chan;
204
205 strncpyz(buf, host, sizeof buf);
206 uhost = buf;
207 nick = splitnick(&uhost);
208 for (chan = chanset; chan; chan = chan->next)
209 for (m = chan->channel.member; m && m->nick[0]; m = m->next)
210 if (!rfc_casecmp(nick, m->nick) && !egg_strcasecmp(uhost, m->userhost))
211 m->user = rec;
212 }
213
214 /* Calculate the memory we should be using
215 */
216 int expmem_chanprog()
217 {
218 register int tot = 0;
219 register tcl_timer_t *t;
220
221 for (t = timer; t; t = t->next)
222 tot += sizeof(tcl_timer_t) + strlen(t->cmd) + 1;
223 for (t = utimer; t; t = t->next)
224 tot += sizeof(tcl_timer_t) + strlen(t->cmd) + 1;
225 return tot;
226 }
227
228 float getcputime()
229 {
230 #ifdef HAVE_GETRUSAGE
231 float stime, utime;
232 struct rusage ru;
233
234 getrusage(RUSAGE_SELF, &ru);
235 utime = ru.ru_utime.tv_sec + (ru.ru_utime.tv_usec / 1000000.00);
236 stime = ru.ru_stime.tv_sec + (ru.ru_stime.tv_usec / 1000000.00);
237 return (utime + stime);
238 #else
239 # ifdef HAVE_CLOCK
240 return (clock() / (CLOCKS_PER_SEC * 1.00));
241 # else
242 return -1.00;
243 # endif
244 #endif
245 }
246
247 /* Dump uptime info out to dcc (guppy 9Jan99)
248 */
249 void tell_verbose_uptime(int idx)
250 {
251 char s[256], s1[121];
252 time_t now2, hr, min;
253
254 now2 = now - online_since;
255 s[0] = 0;
256 if (now2 > 86400) {
257 /* days */
258 sprintf(s, "%d day", (int) (now2 / 86400));
259 if ((int) (now2 / 86400) >= 2)
260 strcat(s, "s");
261 strcat(s, ", ");
262 now2 -= (((int) (now2 / 86400)) * 86400);
263 }
264 hr = (time_t) ((int) now2 / 3600);
265 now2 -= (hr * 3600);
266 min = (time_t) ((int) now2 / 60);
267 sprintf(&s[strlen(s)], "%02d:%02d", (int) hr, (int) min);
268 s1[0] = 0;
269 if (backgrd)
270 strcpy(s1, MISC_BACKGROUND);
271 else {
272 if (term_z)
273 strcpy(s1, MISC_TERMMODE);
274 else if (con_chan)
275 strcpy(s1, MISC_STATMODE);
276 else
277 strcpy(s1, MISC_LOGMODE);
278 }
279 dprintf(idx, "%s %s (%s)\n", MISC_ONLINEFOR, s, s1);
280 }
281
282 /* Dump status info out to dcc
283 */
284 void tell_verbose_status(int idx)
285 {
286 char s[256], s1[121], s2[81];
287 char *vers_t, *uni_t;
288 int i;
289 time_t now2 = now - online_since, hr, min;
290 float cputime;
291 #ifdef HAVE_UNAME
292 struct utsname un;
293
294 if (!uname(&un) < 0) {
295 #endif
296 vers_t = " ";
297 uni_t = "*unknown*";
298 #ifdef HAVE_UNAME
299 } else {
300 vers_t = un.release;
301 uni_t = un.sysname;
302 }
303 #endif
304
305 i = count_users(userlist);
306 dprintf(idx, "I am %s, running %s: %d user%s (mem: %uk).\n",
307 botnetnick, ver, i, i == 1 ? "" : "s",
308 (int) (expected_memory() / 1024));
309
310 s[0] = 0;
311 if (now2 > 86400) {
312 /* days */
313 sprintf(s, "%d day", (int) (now2 / 86400));
314 if ((int) (now2 / 86400) >= 2)
315 strcat(s, "s");
316 strcat(s, ", ");
317 now2 -= (((int) (now2 / 86400)) * 86400);
318 }
319 hr = (time_t) ((int) now2 / 3600);
320 now2 -= (hr * 3600);
321 min = (time_t) ((int) now2 / 60);
322 sprintf(&s[strlen(s)], "%02d:%02d", (int) hr, (int) min);
323 s1[0] = 0;
324 if (backgrd)
325 strcpy(s1, MISC_BACKGROUND);
326 else {
327 if (term_z)
328 strcpy(s1, MISC_TERMMODE);
329 else if (con_chan)
330 strcpy(s1, MISC_STATMODE);
331 else
332 strcpy(s1, MISC_LOGMODE);
333 }
334 cputime = getcputime();
335 if (cputime < 0)
336 sprintf(s2, "CPU: unknown");
337 else {
338 hr = cputime / 60;
339 cputime -= hr * 60;
340 sprintf(s2, "CPU: %02d:%05.2f", (int) hr, cputime); /* Actally min/sec */
341 }
342 dprintf(idx, "%s %s (%s) - %s - %s: %4.1f%%\n", MISC_ONLINEFOR,
343 s, s1, s2, MISC_CACHEHIT,
344 100.0 * ((float) cache_hit) / ((float) (cache_hit + cache_miss)));
345
346 if (admin[0])
347 dprintf(idx, "Admin: %s\n", admin);
348
349 dprintf(idx, "Config file: %s\n", configfile);
350 dprintf(idx, "OS: %s %s\n", uni_t, vers_t);
351 dprintf(idx, "Process ID: %d (parent %d)\n", getpid(), getppid());
352
353 /* info library */
354 dprintf(idx, "%s %s\n", MISC_TCLLIBRARY,
355 ((interp) && (Tcl_Eval(interp, "info library") == TCL_OK)) ?
356 tcl_resultstring() : "*unknown*");
357
358 /* info tclversion/patchlevel */
359 dprintf(idx, "%s %s (%s %s)\n", MISC_TCLVERSION,
360 ((interp) && (Tcl_Eval(interp, "info patchlevel") == TCL_OK)) ?
361 tcl_resultstring() : (Tcl_Eval(interp, "info tclversion") == TCL_OK) ?
362 tcl_resultstring() : "*unknown*", MISC_TCLHVERSION,
363 TCL_PATCH_LEVEL ? TCL_PATCH_LEVEL : "*unknown*");
364
365 if (tcl_threaded())
366 dprintf(idx, "Tcl is threaded.\n");
367 dprintf(idx, "Socket table: %d/%d\n", threaddata()->MAXSOCKS, max_socks);
368 }
369
370 /* Show all internal state variables
371 */
372 void tell_settings(int idx)
373 {
374 char s[1024];
375 int i;
376 struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 };
377
378 dprintf(idx, "Botnet nickname: %s\n", botnetnick);
379 if (firewall[0])
380 dprintf(idx, "Firewall: %s:%d\n", firewall, firewallport);
381 dprintf(idx, "Userfile: %s\n", userfile);
382 dprintf(idx, "Motd: %s\n", motdfile);
383 dprintf(idx, "Directories:\n");
384 #ifndef STATIC
385 dprintf(idx, " Help : %s\n", helpdir);
386 dprintf(idx, " Temp : %s\n", tempdir);
387 dprintf(idx, " Modules: %s\n", moddir);
388 #else
389 dprintf(idx, " Help: %s\n", helpdir);
390 dprintf(idx, " Temp: %s\n", tempdir);
391 #endif
392 fr.global = default_flags;
393
394 build_flags(s, &fr, NULL);
395 dprintf(idx, "%s [%s], %s: %s\n", MISC_NEWUSERFLAGS, s,
396 MISC_NOTIFY, notify_new);
397 if (owner[0])
398 dprintf(idx, "%s: %s\n", MISC_PERMOWNER, owner);
399 for (i = 0; i < max_logs; i++)
400 if (logs[i].filename != NULL) {
401 dprintf(idx, "Logfile #%d: %s on %s (%s: %s)\n", i + 1,
402 logs[i].filename, logs[i].chname,
403 masktype(logs[i].mask), maskname(logs[i].mask));
404 }
405 dprintf(idx, "Ignores last %d minute%s.\n", ignore_time,
406 (ignore_time != 1) ? "s" : "");
407 }
408
409 void reaffirm_owners()
410 {
411 char *p, *q, s[121];
412 struct userrec *u;
413
414 /* Please stop breaking this function. */
415 if (owner[0]) {
416 q = owner;
417 p = strchr(q, ',');
418 while (p) {
419 strncpyz(s, q, (p - q) + 1);
420 rmspace(s);
421 u = get_user_by_handle(userlist, s);
422 if (u)
423 u->flags = sanity_check(u->flags | USER_OWNER);
424 q = p + 1;
425 p = strchr(q, ',');
426 }
427 strcpy(s, q);
428 rmspace(s);
429 u = get_user_by_handle(userlist, s);
430 if (u)
431 u->flags = sanity_check(u->flags | USER_OWNER);
432 }
433 }
434
435 void chanprog()
436 {
437 int i;
438 FILE *f;
439 char s[161], rands[8];
440
441 admin[0] = 0;
442 helpdir[0] = 0;
443 tempdir[0] = 0;
444 conmask = 0;
445
446 for (i = 0; i < max_logs; i++)
447 logs[i].flags |= LF_EXPIRING;
448
449 /* Turn off read-only variables (make them write-able) for rehash */
450 protect_readonly = 0;
451
452 /* Now read it */
453 if (!readtclprog(configfile))
454 fatal(MISC_NOCONFIGFILE, 0);
455
456 for (i = 0; i < max_logs; i++) {
457 if (logs[i].flags & LF_EXPIRING) {
458 if (logs[i].filename != NULL) {
459 nfree(logs[i].filename);
460 logs[i].filename = NULL;
461 }
462 if (logs[i].chname != NULL) {
463 nfree(logs[i].chname);
464 logs[i].chname = NULL;
465 }
466 if (logs[i].f != NULL) {
467 fclose(logs[i].f);
468 logs[i].f = NULL;
469 }
470 logs[i].mask = 0;
471 logs[i].flags = 0;
472 }
473 }
474
475 /* We should be safe now */
476 call_hook(HOOK_REHASH);
477 protect_readonly = 1;
478
479 if (!botnetnick[0])
480 strncpyz(botnetnick, origbotname, HANDLEN + 1);
481
482 if (!botnetnick[0])
483 fatal("I don't have a botnet nick!!\n", 0);
484
485 if (!userfile[0])
486 fatal(MISC_NOUSERFILE2, 0);
487
488 if (!readuserfile(userfile, &userlist)) {
489 if (!make_userfile) {
490 char tmp[178];
491
492 egg_snprintf(tmp, sizeof tmp, MISC_NOUSERFILE, configfile);
493 fatal(tmp, 0);
494 }
495 printf("\n\n%s\n", MISC_NOUSERFILE2);
496 if (module_find("server", 0, 0))
497 printf(MISC_USERFCREATE1, origbotname);
498 printf("%s\n\n", MISC_USERFCREATE2);
499 } else if (make_userfile) {
500 make_userfile = 0;
501 printf("%s\n", MISC_USERFEXISTS);
502 }
503
504 if (helpdir[0])
505 if (helpdir[strlen(helpdir) - 1] != '/')
506 strcat(helpdir, "/");
507
508 if (tempdir[0])
509 if (tempdir[strlen(tempdir) - 1] != '/')
510 strcat(tempdir, "/");
511
512 /* Test tempdir: it's vital. */
513
514 /* Possible file race condition solved by using a random string
515 * and the process id in the filename.
516 * FIXME: This race is only partitially fixed. We could still be
517 * overwriting an existing file / following a malicious
518 * link.
519 */
520 make_rand_str(rands, 7); /* create random string */
521 sprintf(s, "%s.test-%u-%s", tempdir, getpid(), rands);
522 f = fopen(s, "w");
523 if (f == NULL)
524 fatal(MISC_CANTWRITETEMP, 0);
525 fclose(f);
526 unlink(s);
527 reaffirm_owners();
528 check_tcl_event("userfile-loaded");
529 }
530
531 /* Reload the user file from disk
532 */
533 void reload()
534 {
535 if (!file_readable(userfile)) {
536 putlog(LOG_MISC, "*", MISC_CANTRELOADUSER);
537 return;
538 }
539
540 noshare = 1;
541 clear_userlist(userlist);
542 noshare = 0;
543 userlist = NULL;
544 if (!readuserfile(userfile, &userlist))
545 fatal(MISC_MISSINGUSERF, 0);
546 reaffirm_owners();
547 check_tcl_event("userfile-loaded");
548 call_hook(HOOK_READ_USERFILE);
549 }
550
551 void rehash()
552 {
553 call_hook(HOOK_PRE_REHASH);
554 noshare = 1;
555 clear_userlist(userlist);
556 noshare = 0;
557 userlist = NULL;
558 chanprog();
559 }
560
561 /*
562 * Brief venture into timers
563 */
564
565 /* Add a timer
566 */
567 unsigned long add_timer(tcl_timer_t ** stack, int elapse, char *cmd,
568 unsigned long prev_id)
569 {
570 tcl_timer_t *old = (*stack);
571
572 *stack = nmalloc(sizeof **stack);
573 (*stack)->next = old;
574 (*stack)->mins = elapse;
575 (*stack)->cmd = nmalloc(strlen(cmd) + 1);
576 strcpy((*stack)->cmd, cmd);
577 /* If it's just being added back and already had an id,
578 * don't create a new one.
579 */
580 if (prev_id > 0)
581 (*stack)->id = prev_id;
582 else
583 (*stack)->id = timer_id++;
584 return (*stack)->id;
585 }
586
587 /* Remove a timer, by id
588 */
589 int remove_timer(tcl_timer_t ** stack, unsigned long id)
590 {
591 tcl_timer_t *old;
592 int ok = 0;
593
594 while (*stack) {
595 if ((*stack)->id == id) {
596 ok++;
597 old = *stack;
598 *stack = ((*stack)->next);
599 nfree(old->cmd);
600 nfree(old);
601 } else
602 stack = &((*stack)->next);
603 }
604 return ok;
605 }
606
607 /* Check timers, execute the ones that have expired.
608 */
609 void do_check_timers(tcl_timer_t ** stack)
610 {
611 tcl_timer_t *mark = *stack, *old = NULL;
612 char x[16];
613
614 /* New timers could be added by a Tcl script inside a current timer
615 * so i'll just clear out the timer list completely, and add any
616 * unexpired timers back on.
617 */
618 *stack = NULL;
619 while (mark) {
620 if (mark->mins > 0)
621 mark->mins--;
622 old = mark;
623 mark = mark->next;
624 if (!old->mins) {
625 egg_snprintf(x, sizeof x, "timer%lu", old->id);
626 do_tcl(x, old->cmd);
627 nfree(old->cmd);
628 nfree(old);
629 } else {
630 old->next = *stack;
631 *stack = old;
632 }
633 }
634 }
635
636 /* Wipe all timers.
637 */
638 void wipe_timers(Tcl_Interp *irp, tcl_timer_t **stack)
639 {
640 tcl_timer_t *mark = *stack, *old;
641
642 while (mark) {
643 old = mark;
644 mark = mark->next;
645 nfree(old->cmd);
646 nfree(old);
647 }
648 *stack = NULL;
649 }
650
651 /* Return list of timers
652 */
653 void list_timers(Tcl_Interp *irp, tcl_timer_t *stack)
654 {
655 char mins[10], id[16], *x;
656 EGG_CONST char *argv[3];
657 tcl_timer_t *mark;
658
659 for (mark = stack; mark; mark = mark->next) {
660 egg_snprintf(mins, sizeof mins, "%u", mark->mins);
661 egg_snprintf(id, sizeof id, "timer%lu", mark->id);
662 argv[0] = mins;
663 argv[1] = mark->cmd;
664 argv[2] = id;
665 x = Tcl_Merge(3, argv);
666 Tcl_AppendElement(irp, x);
667 Tcl_Free((char *) x);
668 }
669 }
670
671 /* Oddly enough, written by Sup (former(?) Eggdrop coder)
672 */
673 int isowner(char *name)
674 {
675 register char *ptr = NULL, *s = NULL, *n = NULL;
676
677 if (!name)
678 return 0;
679
680 ptr = owner - 1;
681
682 do {
683 ptr++;
684 if (*ptr && !egg_isspace(*ptr) && *ptr != ',') {
685 if (!s)
686 s = ptr;
687 } else if (s) {
688 for (n = name; *n && *s && s < ptr &&
689 tolower((unsigned) *n) == tolower((unsigned) *s); n++, s++);
690
691 if (s == ptr && !*n)
692 return 1;
693
694 s = NULL;
695 }
696 } while (*ptr);
697
698 return 0;
699 }

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23