1 | 1 | simandl | # |
2 | | | # $Id: HotSaNICmod.pm,v 1.21 2004/02/27 09:21:47 bernisys Exp $ |
3 | | | # |
4 | | | |
5 | | | package HotSaNICmod; |
6 | | | |
7 | | | use HotSaNICparser; |
8 | | | use RRDs; |
9 | | | |
10 | | | my $MODNAME; |
11 | | | my %MODARGS; |
12 | | | |
13 | | | ($VERSION = '$Revision: 1.21 $') =~ s/.*(\d+\.\d+).*/$1/; |
14 | | | |
15 | | | ###################################################################### |
16 | | | # |
17 | | | # control function to make sure that a module is running only once |
18 | | | # |
19 | | | # Usage: |
20 | | | # dupe_control($what,$module-name,$message); |
21 | | | # |
22 | | | # $what: |
23 | | | # start -> call this method at the head of a module to touch the |
24 | | | # dupe-detection checkfile. |
25 | | | # stop -> remove checkfile and exit normally. |
26 | | | # warn -> print $message as warning and continue execution. |
27 | | | # die -> remove checkfile and stop process with error. |
28 | | | # |
29 | | | # The given message will be preceded with a timestamp (in seconds |
30 | | | # since 1970) and the module-name: |
31 | | | # |
32 | | | # <time> MODULE:message\n |
33 | | | # |
34 | | | sub dupe_control { |
35 | | | my ($what,$mod,$message)=@_; |
36 | | | if (!defined $mod) { $mod="(unknown module)"; } |
37 | | | if (!defined $message) { $message="(unknown reason)"; } |
38 | | | my $pid=0; |
39 | | | my $delta=0; |
40 | | | my $line; |
41 | | | |
42 | | | if ( ($what eq "check") || ($what eq "start") ) { $pid=HotSaNICparser::get_pid($MODARGS{DEBUGLEVEL}); } |
43 | | | |
44 | | | if ($what eq "start") { |
45 | | | if ($pid > 0) { die time." $mod: process already running on PID $pid for $delta seconds.\n"; } |
46 | | | open FILE,">running.pid"; |
47 | | | print FILE $$; |
48 | | | close FILE; |
49 | | | } |
50 | | | elsif ($what eq "stop") { |
51 | | | if (-e "running.pid") { unlink "running.pid"; } |
52 | | | exit 0; |
53 | | | } |
54 | | | elsif ($what eq "die") { |
55 | | | if (-e "running.pid") { unlink "running.pid"; } |
56 | | | die time." $mod: $message\n"; |
57 | | | } |
58 | | | elsif ($what eq "warn") { |
59 | | | print time," $mod warn: $message\n"; |
60 | | | } |
61 | | | elsif ($what eq "check") { |
62 | | | if (!defined $pid) { $pid=0; } |
63 | | | if (($pid > 0) && (-e "/proc/$pid/stat")) { |
64 | | | open FILE,"/proc/$pid/stat"; |
65 | | | @fileds=split / /,<FILE>; |
66 | | | close FILE; |
67 | | | $starttime=$fileds[21]/100; |
68 | | | open FILE,"/proc/uptime"; |
69 | | | $line=<FILE>; |
70 | | | close FILE; |
71 | | | ($uptime)=split / /,$line; |
72 | | | $delta=int(($uptime-$starttime)*100)/100; |
73 | | | } |
74 | | | if (($pid > 0) && ($^O =~ /bsd/)) { |
75 | | | open FILE,"/proc/$pid/status"; |
76 | | | @fileds=split / /,<FILE>; |
77 | | | close FILE; |
78 | | | ($starttime, $msec)= split /,/, $fileds[7]; |
79 | | | $delta=int((time-$starttime)*100)/100; |
80 | | | } |
81 | | | if (!defined $delta) { $delta=0; } |
82 | | | return ($pid,$delta); |
83 | | | } |
84 | | | else { print time," $mod: method \"$what\" not supported.\n"; } |
85 | | | } |
86 | | | |
87 | | | |
88 | | | ###################################################################### |
89 | | | # |
90 | | | # check cmdline arguments |
91 | | | # |
92 | | | sub init { |
93 | | | $args=shift || ""; |
94 | | | $MODNAME=HotSaNICparser::get_module_name().""; |
95 | | | |
96 | | | my $found=0; |
97 | | | my @possible_args=("start","stop","status","configure","sample","update","version","help","showargs"); |
98 | | | foreach (@possible_args) { |
99 | | | if ($args eq $_) { $found++; } |
100 | | | } |
101 | | | if ($found == 0) { $args=""; } |
102 | | | |
103 | | | # import common functions |
104 | | | # |
105 | | | my $COM_LIB="./platform/common.pm"; |
106 | | | if ( -e $COM_LIB ) { |
107 | | | eval { require $COM_LIB; }; |
108 | | | if ($@) { HotSaNICmod::dupe_control("die",$MODNAME,"Error importing common library: $COM_LIB.\n\t$!\n$@\n"); } |
109 | | | } |
110 | | | else { HotSaNICmod::dupe_control("die",$MODNAME,"Error importing common library: $COM_LIB.\n\tfile not found.\n"); } |
111 | | | |
112 | | | configure(); |
113 | | | |
114 | | | # import OS-specific functions |
115 | | | # |
116 | | | my $fallback=0; |
117 | | | my $OS_LIB="./platform/$^O.pm"; |
118 | | | if ( -e $OS_LIB ) { |
119 | | | eval { require $OS_LIB; }; |
120 | | | if ($@) { print "Error importing library: $OS_LIB\n\t$!\n$@\n"; $fallback=1; } |
121 | | | } |
122 | | | else { print "$MODNAME: Operating system \"$^O\" not supported!\n"; $fallback=1; } |
123 | | | |
124 | | | # OS-lib not found -> try to load default lib |
125 | | | # |
126 | | | if ($fallback == 1) { |
127 | | | print "$MODNAME: Falling back to \"default\"\n"; |
128 | | | eval { require "./platform/default.pm"; }; |
129 | | | if ($@) { HotSaNICmod::dupe_control("die",$MODNAME,"Error importing default module.\n$@\n"); } |
130 | | | } |
131 | | | |
132 | | | if ( $args eq "" ) { |
133 | | | print "arguments missing!\n"; |
134 | | | print "usage: $0 [start/stop/status/configure/sample/update/version/help/showargs]\n"; |
135 | | | print "\n"; |
136 | | | } |
137 | | | |
138 | | | if ( $args =~ /help/) { |
139 | | | print "usage: $0 [start/stop/status/configure/sample/update/version/help/showargs]\n"; |
140 | | | print "\n"; |
141 | | | print "alternative usage: send a signal to <module PID>\n"; |
142 | | | print "\n"; |
143 | | | print "argument signal function\n"; |
144 | | | print "--------- ------- --------------------------------------------------\n"; |
145 | | | print "start start module daemon\n"; |
146 | | | print "stop SIGTERM terminate module daemon\n"; |
147 | | | print "status show status of module daemon\n"; |
148 | | | print "configure SIGHUP daemon re-reads its config file\n"; |
149 | | | print "sample SIGUSR1 sample now\n"; |
150 | | | print "update SIGUSR2 (not implemented yet)\n"; |
151 | | | print "version show version of OS-dependant module used\n"; |
152 | | | print "showargs show settings hash\n"; |
153 | | | print "\n"; |
154 | | | } |
155 | | | |
156 | | | if ( ($args =~ /version/) or ($MODARGS{DEBUGLEVEL} > 0) ) { |
157 | | | push my @VERSIONS,HotSaNICmod::OSdep::version() if defined &HotSaNICmod::OSdep::version; |
158 | | | push @VERSIONS,HotSaNICmod::common::version() if defined &HotSaNICmod::common::version; |
159 | | | push @VERSIONS,syssnmp::version() if defined &syssnmp::version; |
160 | | | print "$MODNAME: using libs: ",join(" / ",@VERSIONS),"\n"; |
161 | | | } |
162 | | | |
163 | | | if ( $args =~ /start/) { |
164 | | | my ($pid,$uptime)=HotSaNICmod::dupe_control("check",$MODNAME,""); |
165 | | | if ($pid > 0) { print "$MODNAME: module already running on PID $pid\n"; } |
166 | | | else { |
167 | | | HotSaNICmod::daemonize(); |
168 | | | HotSaNICmod::dupe_control("start",$MODNAME,""); |
169 | | | while (1 == 1) { sleep; } |
170 | | | } |
171 | | | } |
172 | | | |
173 | | | if ( $args =~ /status/) { |
174 | | | my ($pid,$uptime)=HotSaNICmod::dupe_control("check",$MODNAME,""); |
175 | | | if ($pid > 0) { |
176 | | | print "$MODNAME: module running on PID $pid for $uptime sec.\n"; |
177 | | | } |
178 | | | else { print "$MODNAME: no process running.\n"; } |
179 | | | my @DBs=HotSaNICmod::get_DBs(); |
180 | | | my ($min,$max)=HotSaNICmod::get_last_DB_changes(); |
181 | | | if ($min>=0) { |
182 | | | print "last DB update: $min"; |
183 | | | if ($max != $min) { print " (max: $max)"; } |
184 | | | print " seconds ago\n"; |
185 | | | print "DBs found: ",join("\n ",@DBs),"\n"; |
186 | | | } |
187 | | | } |
188 | | | |
189 | | | if ( $args =~ /sample/) { |
190 | | | my ($pid,$uptime)=HotSaNICmod::dupe_control("check",$MODNAME,""); |
191 | | | if ($pid > 0) { kill "SIGUSR1",$pid; } |
192 | | | else { print "$MODNAME: no process running.\n"; } |
193 | | | } |
194 | | | |
195 | | | if ( $args =~ /stop/) { |
196 | | | my ($pid,$uptime)=HotSaNICmod::dupe_control("check",$MODNAME,""); |
197 | | | if ($pid > 0) { kill "SIGTERM",$pid; } |
198 | | | else { print "$MODNAME: no process running.\n"; } |
199 | | | } |
200 | | | |
201 | | | if ( $args =~ /configure/) { |
202 | | | my ($pid,$uptime)=HotSaNICmod::dupe_control("check",$MODNAME,""); |
203 | | | if ($pid > 0) { kill "SIGHUP",$pid; } |
204 | | | else { print "$MODNAME: no process running.\n"; } |
205 | | | } |
206 | | | |
207 | | | if ( $args =~ /update/) { |
208 | | | my ($pid,$uptime)=HotSaNICmod::dupe_control("check",$MODNAME,""); |
209 | | | if ($pid > 0) { kill "SIGUSR2",$pid; } |
210 | | | else { print "$MODNAME: no process running.\n"; } |
211 | | | } |
212 | | | |
213 | | | if ( $args =~ /showargs/) { |
214 | | | for (sort keys %MODARGS) { print "$_ = ".$MODARGS{$_}."\n"; } |
215 | | | } |
216 | | | } |
217 | | | |
218 | | | |
219 | | | ###################################################################### |
220 | | | # |
221 | | | # fork into background |
222 | | | # |
223 | | | sub daemonize { |
224 | | | # set signal handlers and create background childprocess |
225 | | | # |
226 | | | $SIG{TERM} = \&terminate; |
227 | | | $SIG{HUP} = \&configure; |
228 | | | $SIG{USR1} = \&sample; |
229 | | | $SIG{USR2} = \&update; |
230 | | | fork && exit 0; |
231 | | | } |
232 | | | |
233 | | | ###################################################################### |
234 | | | # |
235 | | | # terminate module daemon |
236 | | | # |
237 | | | sub terminate { |
238 | | | HotSaNICmod::dupe_control("stop",$MODNAME,""); |
239 | | | } |
240 | | | |
241 | | | ###################################################################### |
242 | | | # |
243 | | | # interface to OS-dependent functions |
244 | | | # |
245 | | | sub sample { |
246 | | | HotSaNICmod::OSdep::sample(%MODARGS); |
247 | | | } |
248 | | | |
249 | | | sub update { |
250 | | | HotSaNICmod::OSdep::update; |
251 | | | } |
252 | | | |
253 | | | ###################################################################### |
254 | | | # |
255 | | | # configure |
256 | | | # - parse main settings |
257 | | | # - parse module settings |
258 | | | # - set module-specific configuration |
259 | | | sub configure { |
260 | | | my @CONFmod=HotSaNICparser::read_settings(".",$MODNAME); |
261 | | | my %CONFglob=HotSaNICparser::get_config("../..",$MODNAME); |
262 | | | if (scalar(@CONFmod) == 0) { HotSaNICmod::dupe_control("die",$MODNAME,"missing settings file"); } |
263 | | | if (scalar(keys(%CONFglob)) == 0) { HotSaNICmod::dupe_control("die",$MODNAME,"missing settings file"); } |
264 | | | if (! -d $CONFglob{VARDIR}."/modules/system") { mkdir $CONFglob{VARDIR}."/modules/system",0755; } |
265 | | | |
266 | | | # configure module-specific settings |
267 | | | # |
268 | | | %MODARGS=HotSaNICmod::common::configure(@CONFmod); |
269 | | | |
270 | | | # configure common module settings |
271 | | | # |
272 | | | foreach (@CONFmod) { |
273 | | | ($var,$value)=HotSaNICparser::parse_line($_); |
274 | | | if ($var eq "DEBUGLEVEL") { $MODARGS{DEBUGLEVEL}=$value; } |
275 | | | } |
276 | | | |
277 | | | # add global settings |
278 | | | # |
279 | | | $MODARGS{MODNAME}=$MODNAME; |
280 | | | $MODARGS{VARDIR}=$CONFglob{VARDIR}."/modules/system"; |
281 | | | $MODARGS{SNMPWALK}=$CONFglob{SNMPWALK}; |
282 | | | $MODARGS{SNMPGET}=$CONFglob{SNMPGET}; |
283 | | | if ( (!defined $MODARGS{DEBUGLEVEL}) || ($CONFglob{DEBUGLEVEL} > $MODARGS{DEBUGLEVEL}) ) { $MODARGS{DEBUGLEVEL}=$CONFglob{DEBUGLEVEL}; } |
284 | | | if (!defined $MODARGS{DEBUGLEVEL}) { $MODARGS{DEBUGLEVEL}=-1; } |
285 | | | |
286 | | | } |
287 | | | |
288 | | | ###################################################################### |
289 | | | # |
290 | | | # returns an array containing all "*.rrd" files in the current module's "rrd" directory |
291 | | | # |
292 | | | sub get_DBs { |
293 | | | opendir(DIR,"rrd") || HotSaNICmod::dupe_control("die",$MODARGS{"MODNAME"},"error opening database dir - $!"); |
294 | | | my @DBs=grep(/\.rrd/,readdir(DIR)); |
295 | | | closedir(DIR); |
296 | | | return @DBs; |
297 | | | } |
298 | | | |
299 | | | |
300 | | | ###################################################################### |
301 | | | # |
302 | | | # returns an array containing the min and max seconds since last DB changes |
303 | | | # |
304 | | | # if no DBs are found, (-1,-1) will be returned. |
305 | | | # |
306 | | | sub get_last_DB_changes { |
307 | | | |
308 | | | my @DBs=get_DBs(); |
309 | | | |
310 | | | if (!@DBs) { return (-1,-1); } |
311 | | | |
312 | | | my ($min,$max)=(999999999999,0); |
313 | | | foreach $test (@DBs) { |
314 | | | (undef,undef,undef,undef,undef,undef,undef,undef,undef,$mtime,undef,undef,undef)=stat("rrd/".$test); |
315 | | | $mtime=time-$mtime; |
316 | | | if ($mtime<$min) { $min=$mtime; } |
317 | | | if ($mtime>$max) { $max=$mtime; } |
318 | | | } |
319 | | | |
320 | | | return ($min,$max); |
321 | | | } |
322 | | | |
323 | | | |
324 | | | ###################################################################### |
325 | | | # |
326 | | | # updates a database, creates a new one if necessary |
327 | | | # |
328 | | | # USAGE: |
329 | | | # |
330 | | | # do_rrd($database,$maxvalue,$sampletime,@values); |
331 | | | # |
332 | | | # $database name of the database (without ".rrd" suffix) |
333 | | | # |
334 | | | # $maxvalue the maximum value to be expected on this datasource |
335 | | | # (needed for DB creation) |
336 | | | # |
337 | | | # $sampletime timestamp of the current sample |
338 | | | # |
339 | | | # @values array of all values for this timestamp |
340 | | | # |
341 | | | sub do_rrd { |
342 | | | my $dbname = shift; |
343 | | | my $dbmax = shift; |
344 | | | my $time = shift; |
345 | | | my $values=join(":",@_); |
346 | | | |
347 | | | # build new database if needed |
348 | | | if ( ! -e "rrd/$dbname.rrd" ) { system("./makerrd","$dbname","$dbmax") } |
349 | | | |
350 | | | # update database |
351 | | | RRDs::update "rrd/$dbname.rrd", $time.":".$values; |
352 | | | if ($ERROR = RRDs::error) { print $time," ",$MODNAME,": unable to update '$dbname.rrd': $ERROR\n"; } |
353 | | | } |
354 | | | |
355 | | | 1; |
356 | | | |