]> git.donarmstrong.com Git - infobot.git/blob - src/modules.pl
ws
[infobot.git] / src / modules.pl
1 #
2 #  modules.pl: pseudo-Module handler
3 #      Author: dms
4 #     Version: v0.2 (20000629)
5 #     Created: 20000624
6 #
7
8 # use strict;   # TODO
9
10 use vars qw($AUTOLOAD);
11
12 ###
13 ### REQUIRED MODULES.
14 ###
15
16 eval "use IO::Socket";
17 if ($@) {
18     &ERROR("no IO::Socket?");
19     exit 1;
20 }
21 &showProc(" (IO::Socket)");
22
23 ### MODULES.
24 %myModules = (
25         "bzflag"        => "BZFlag.pl",
26         "countdown"     => "Countdown.pl",
27         "debian"        => "Debian.pl",
28         "debianExtra"   => "DebianExtra.pl",
29         "dict"          => "Dict.pl",
30         "dumpvars"      => "DumpVars.pl",
31         "symdump"       => "DumpVars2.pl",
32         "exchange"      => "Exchange.pl",
33         "factoids"      => "Factoids.pl",
34         "kernel"        => "Kernel.pl",
35         "perlMath"      => "Math.pl",
36         "news"          => "News.pl",
37         "plug"          => "Plug.pl",
38         "quote"         => "Quote.pl",
39         "rootwarn"      => "RootWarn.pl",
40         "search"        => "Search.pl",
41         "slashdot"      => "Slashdot3.pl",
42         "topic"         => "Topic.pl",
43         "units"         => "Units.pl",
44         "uptime"        => "Uptime.pl",
45         "ircdcc"        => "UserDCC.pl",
46         "userinfo"      => "UserInfo.pl",
47         "weather"       => "Weather.pl",
48         "wwwsearch"     => "W3Search.pl",
49         "whatis"        => "WhatIs.pl",
50         "wingate"       => "Wingate.pl",
51         "babelfish"     => "babel.pl",
52         "insult"        => "insult.pl",
53         "nickometer"    => "nickometer.pl",
54         "zfi"           => "zfi.pl",
55         "zippy"         => "Zippy.pl",
56         "zsi"           => "zsi.pl",
57         "botmail"       => "botmail.pl",
58         "httpdtype"     => "HTTPDtype.pl",
59 );
60 ### THIS IS NOT LOADED ON RELOAD :(
61 my @myModulesLoadNow;
62 my @myModulesReloadNot;
63 BEGIN {
64     @myModulesLoadNow   = ('topic', 'uptime', 'news', 'rootWarn', 'symdump', 'botmail');
65     @myModulesReloadNot = ('IRC/Irc.pl','IRC/Schedulers.pl');
66 }
67
68 sub loadCoreModules {
69     my @mods = &getPerlFiles($bot_src_dir);
70
71     &status("Loading CORE modules...");
72
73     foreach (sort @mods) {
74         my $mod = "$bot_src_dir/$_";
75
76         eval "require \"$mod\"";
77         if ($@) {
78             &ERROR("lCM => $@");
79             &shutdown();
80             exit 1;
81         }
82
83         $moduleAge{$mod} = (stat $mod)[9];
84         &showProc(" ($_)") if (&IsParam("DEBUG"));
85     }
86 }
87
88 sub loadDBModules {
89     my $f;
90     # todo: use function to load module.
91
92     if ($param{'DBType'} =~ /^(mysql|SQLite|pgsql)$/i) {
93         eval "use DBI";
94         if ($@) {
95             &ERROR("No support for DBI::" . $param{'DBType'} . ", exiting!");
96             exit 1;
97         }
98         &status("Loading " . $param{'DBType'} . " support.");
99         $f = "$bot_src_dir/dbi.pl";
100         require $f;
101         $moduleAge{$f} = (stat $f)[9];
102
103         &showProc(" (DBI::" . $param{'DBType'} . ")");
104     } else {
105         &WARN("DB support DISABLED.");
106         return;
107     }
108 }
109
110 sub loadFactoidsModules {
111     if (!&IsParam("factoids")) {
112         &status("Factoid support DISABLED.");
113         return;
114     }
115
116     &status("Loading Factoids modules...");
117
118     foreach ( &getPerlFiles("$bot_src_dir/Factoids") ) {
119         my $mod = "$bot_src_dir/Factoids/$_";
120
121         eval "require \"$mod\"";
122         if ($@) {
123             &ERROR("lFM: $@");
124             exit 1;
125         }
126
127         $moduleAge{$mod} = (stat $mod)[9];
128         &showProc(" ($_)") if (&IsParam("DEBUG"));
129     }
130 }
131
132 sub loadIRCModules {
133     my ($interface) = &whatInterface();
134     if ($interface =~ /IRC/) {
135         &status("Loading IRC modules...");
136
137         eval "use Net::IRC";
138         if ($@) {
139             &ERROR("libnet-irc-perl is not installed!");
140             exit 1;
141         }
142         &showProc(" (Net::IRC)");
143     } else {
144         &status("IRC support DISABLED.");
145         # disabling forking. Why?
146         #$param{forking}        = 0;
147         #$param{noSHM}  = 1;
148     }
149
150     foreach ( &getPerlFiles("$bot_src_dir/$interface") ) {
151         my $mod = "$bot_src_dir/$interface/$_";
152
153         # hrm... use another config option besides DEBUG to display
154         # change in memory usage.
155         &status("Loading Modules \"$mod\"") if (!&IsParam("DEBUG"));
156         eval "require \"$mod\"";
157         if ($@) {
158             &ERROR("require \"$mod\" => $@");
159             &shutdown();
160             exit 1;
161         }
162
163         $moduleAge{$mod} = (stat $mod)[9];
164         &showProc(" ($_)") if (&IsParam("DEBUG"));
165     }
166 }
167
168 sub loadMyModulesNow {
169     my $loaded = 0;
170     my $total  = 0;
171
172     &status("Loading MyModules...");
173     foreach (@myModulesLoadNow) {
174         $total++;
175         if (!defined $_) {
176             &WARN("mMLN: null element.");
177             next;
178         }
179
180         if (!&IsParam($_) and !&IsChanConf($_) and !&getChanConfList($_)) {
181             if (exists $myModules{$_}) {
182                 &status("myModule: $myModules{$_} or $_ (1) not loaded.");
183             } else {
184                 &DEBUG("myModule: $_ (2) not loaded.");
185             }
186
187             next;
188         }
189
190         # weird hack to get rootwarn to work.
191         # it may break on other cases though, any ideas?
192         &loadMyModule( $myModules{$_} || $myModules{lc $_} );
193         $loaded++;
194     }
195
196     &status("Module: Runtime: Loaded/Total [$loaded/$total]");
197 }
198
199 ### rename to moduleReloadAll?
200 sub reloadAllModules {
201     my $retval = "";
202
203     &VERB("Module: reloading all.",2);
204
205     # obscure usage of map and regex :)
206     foreach (map { s/.*?\/?src/src/; $_ } keys %moduleAge) {
207         $retval .= &reloadModule($_);
208     }
209
210     &VERB("Module: reloading done.",2);
211     return $retval;
212 }
213
214 ### rename to modulesReload?
215 sub reloadModule {
216     my ($mod)   = @_;
217     my $file    = (grep /\/$mod/, keys %INC)[0];
218     my $retval = "";
219
220     # don't reload if it's not our module.
221     if ($mod =~ /::/ or $mod !~ /pl$/) {
222         &VERB("Not reloading $mod.",3);
223         return $retval;
224     }
225
226     if (!defined $file) {
227         &WARN("rM: Cannot reload $mod since it was not loaded anyway.");
228         return $retval;
229     }
230
231     if (! -f $file) {
232         &ERROR("rM: file '$file' does not exist?");
233         return $retval;
234     }
235
236     if (grep /$mod/, @myModulesReloadNot) {
237         &DEBUG("rM: should not reload $mod");
238         return $retval;
239     }
240
241     my $age = (stat $file)[9];
242
243     if (!exists $moduleAge{$file}) {
244         &DEBUG("Looks like $file was not loaded; fixing.");
245     } else {
246         return $retval if ($age == $moduleAge{$file});
247
248         if ($age < $moduleAge{$file}) {
249             &WARN("rM: we're not gonna downgrade '$file'; use touch.");
250             &DEBUG("age => $age");
251             &DEBUG("mA{$file} => $moduleAge{$file}");
252             return $retval;
253         }
254
255         my $dc  = &Time2String($age   - $moduleAge{$file});
256         my $ago = &Time2String(time() - $moduleAge{$file});
257
258         &VERB("Module:  delta change: $dc",2);
259         &VERB("Module:           ago: $ago",2);
260     }
261
262     &status("Module: Loading $mod...");
263
264     delete $INC{$file};
265     eval "require \"$file\"";   # require or use?
266     if (@$) {
267         &DEBUG("rM: failure: @$ ");
268     } else {
269         my $basename = $file;
270         $basename =~ s/^.*\///;
271         &status("Module: reloaded $basename");
272         $retval = " $basename";
273         $moduleAge{$file} = $age;
274     }
275     return $retval;
276 }
277
278 ###
279 ### OPTIONAL MODULES.
280 ###
281
282 my %perlModulesLoaded  = ();
283 my %perlModulesMissing = ();
284
285 sub loadPerlModule {
286     return 0 if (exists $perlModulesMissing{$_[0]});
287     &reloadModule($_[0]);
288     return 1 if (exists $perlModulesLoaded{$_[0]});
289
290     eval "use $_[0]";
291     if ($@) {
292         &WARN("Module: $_[0] is not installed!");
293         $perlModulesMissing{$_[0]} = 1;
294         return 0;
295     } else {
296         $perlModulesLoaded{$_[0]} = 1;
297         &status("Loaded $_[0]");
298         &showProc(" ($_[0])");
299         return 1;
300     }
301 }
302
303 sub loadMyModule {
304     my ($tmp) = @_;
305     if (!defined $tmp) {
306         &WARN("loadMyModule: module is NULL.");
307         return 0;
308     }
309
310     my ($modulename, $modulebase);
311     if (exists $myModules{$tmp}) {
312         ($modulename, $modulebase) = ($tmp, $myModules{$tmp});
313     } else {
314         $modulebase = $tmp;
315         if ($tmp = grep /^$modulebase$/, keys %myModules) {
316             &DEBUG("lMM: lame hack, file => name => $tmp.");
317             $modulename = $tmp;
318         }
319     }
320     my $modulefile = "$bot_src_dir/Modules/$modulebase";
321
322     # call reloadModule() which checks age of file and reload.
323     if (grep /\/$modulebase$/, keys %INC) {
324         &reloadModule($modulebase);
325         return 1;       # depend on reloadModule?
326     }
327
328     if (! -f $modulefile) {
329         &ERROR("lMM: module ($modulebase) does not exist.");
330         if ($$ == $bot_pid) {   # parent.
331             &shutdown() if (defined $shm and defined $dbh);
332         } else {                        # child.
333             &DEBUG("b4 delfork 1");
334             &delForked($modulename);
335         }
336
337         exit 1;
338     }
339
340     eval "require \"$modulefile\"";
341     if ($@) {
342         &ERROR("cannot load my module: $modulebase");
343         if ($bot_pid != $$) {   # child.
344             &DEBUG("b4 delfork 2");
345             &delForked($modulename);
346             exit 1;
347         }
348
349         return 0;
350     } else {
351         $moduleAge{$modulefile} = (stat $modulefile)[9];
352
353         &status("Loaded $modulebase");
354         &showProc(" ($modulebase)");
355         return 1;
356     }
357 }
358
359 $no_timehires = 0;
360 eval "use Time::HiRes qw(gettimeofday tv_interval)";
361 if ($@) {
362     &WARN("No Time::HiRes?");
363     $no_timehires = 1;
364 }
365 &showProc(" (Time::HiRes)");
366
367 sub AUTOLOAD {
368     if (!defined $AUTOLOAD and defined $::AUTOLOAD) {
369         &DEBUG("AUTOLOAD: hrm.. ::AUTOLOAD defined!");
370     }
371     return unless (defined $AUTOLOAD);
372     return if ($AUTOLOAD =~ /__/);      # internal.
373
374     my $str = join(', ', @_);
375     my ($package, $filename, $line) = caller;
376     &ERROR("UNKNOWN FUNCTION CALLED: $AUTOLOAD ($str) $filename line $line");
377
378     $AUTOLOAD =~ s/^(\S+):://g;
379
380     if (exists $myModules{lc $AUTOLOAD}) {
381         # hopefully this will work.
382         &DEBUG("Trying to load module $AUTOLOAD...");
383         &loadMyModule(lc $AUTOLOAD);
384     }
385 }
386
387 sub getPerlFiles {
388     my($dir) = @_;
389
390     if (!opendir(DIR, $dir)) {
391         &ERROR("Cannot open source directory ($dir): $!");
392         exit 1;
393     }
394
395     my @mods;
396     while (defined(my $file = readdir DIR)) {
397         next unless $file =~ /\.pl$/;
398         next unless $file =~ /^[A-Z]/;
399         push(@mods, $file);
400     }
401     closedir DIR;
402
403     return reverse sort @mods;
404 }
405
406 1;