1package NetSNMP::manager;
2
3use strict ();
4use warnings;
5use Apache::Constants qw(:common);
6use CGI qw(:standard delete_all);
7use SNMP ();
8use DBI ();
9use NetSNMP::manager::displaytable qw(displaytable displaygraph);
10
11# globals
12$NetSNMP::manager::hostname = 'localhost';          # Host that serves the mSQL Database
13$NetSNMP::manager::dbname = 'snmp';                 # mySQL Database name
14$NetSNMP::manager::user = 'root';
15# $NetSNMP::manager::pass = "password";
16$NetSNMP::manager::imagebase = "/home/hardaker/src/snmp/manager";	# <=== CHANGE ME ====
17$NetSNMP::manager::redimage = "/graphics/red.gif";
18$NetSNMP::manager::greenimage = "/graphics/green.gif";
19#$NetSNMP::manager::verbose = 1;
20$NetSNMP::manager::tableparms  = "border=1 bgcolor=\"#c0c0e0\"";
21$NetSNMP::manager::headerparms = "border=1 bgcolor=\"#b0e0b0\"";
22
23# init the snmp library
24$SNMP::save_descriptions=1;
25#SNMP::init_mib();
26
27%NetSNMP::manager::myorder = qw(id 0 oidindex 1 host 2 updated 3);
28
29sub handler {
30    my $r = shift;
31    Apache->request($r);
32
33    # get info from handler
34    my $hostname = $r->dir_config('hostname') || $NetSNMP::manager::hostname;
35    my $dbname = $r->dir_config('dbname') || $NetSNMP::manager::dbname;
36    my $sqluser = $r->dir_config('user') || $NetSNMP::manager::user;
37    my $pass = $r->dir_config('pass') || $NetSNMP::manager::pass;
38    my $verbose = $r->dir_config('verbose') || $NetSNMP::manager::verbose;
39
40#===========================================================================
41#  Global defines
42#===========================================================================
43
44my ($dbh, $query, $remuser);
45
46$remuser = $ENV{'REMOTE_USER'};
47$remuser = "guest" if (!defined($remuser) || $remuser eq "");
48
49#===========================================================================
50# Connect to the mSQL database with the appropriate driver
51#===========================================================================
52($dbh = DBI->connect("DBI:mysql:database=$dbname;host=$hostname", $sqluser, $pass))
53    or die "\tConnect not ok: $DBI::errstr\n";
54
55#===========================================================================
56# stats Images, for inclusion on another page. (ie, slashdot user box)
57#===========================================================================
58if (my $group = param('groupstat')) {
59    $r->content_type("image/gif");
60    $r->send_http_header();
61    my $cur = getcursor($dbh, "select host from usergroups as ug, hostgroups as hg where ug.groupname = '$group' and hg.groupname = '$group' and user = '$remuser'");
62    while (my $row = $cur->fetchrow_hashref ) {
63	if (checkhost($dbh, $group, $row->{'host'})) {
64	    open(I, "$NetSNMP::manager::imagebase$NetSNMP::manager::redimage");
65	    while(read(I, $_, 4096)) { print; }
66	    close(I);
67	}
68    }
69    open(I, "$NetSNMP::manager::imagebase$NetSNMP::manager::greenimage");
70    while(read(I, $_, 4096)) { print; }
71    close(I);
72    return OK();
73}
74
75
76sub date_format {
77    my $time = shift;
78    my @out = localtime($time);
79    my $ret = $out[4] . "-" . $out[3] . "-" . $out[5] . " " . $out[2] . " " . $out[1];
80#    print STDERR "$time: $ret\n";
81    return $ret;
82}
83
84
85#
86# Graphing of historical data
87#
88if ((param('displaygraph') || param('dograph')) && param('table')) {
89    my $host = param('host');
90    my $group = param('group');
91    if (!isuser($dbh, $remuser, $group)) {
92	$r->content_type("image/png");
93	$r->send_http_header();
94	print "Unauthorized access to that group ($group)\n";
95	return Exit($dbh, $group);
96    }
97    my $table = param('table');
98    my @columns;
99
100    if (!param('dograph')) {
101	$r->content_type("text/html");
102	$r->send_http_header();
103	print "<body bgcolor=\"#ffffff\">\n";
104	print "<form>\n";
105	print "<table border=1><tr><td>\n";
106
107	print "<table>\n";
108	print "<tr align=top><th></th><th>Select indexes<br>to graph</th></tr>\n";
109
110	my $handle = getcursor($dbh, "SELECT sql_small_result distinct(oidindex) FROM $table where host = '$host'");
111	my @cols;
112	while (  $row = $handle->fetchrow_hashref ) {
113	    print "<tr><td>$row->{oidindex}</td><td><input type=checkbox value=1 name=" . 'graph_' . displaytable::to_unique_key($row->{'oidindex'}) . "></td></tr>\n";
114	}
115	print "</table>\n";
116
117	print "</td><td>\n";
118
119	print "<table>\n";
120	print "<tr align=top><th></th><th>Select Columns<br>to graph</th></tr>\n";
121	my $handle = getcursor($dbh, "SELECT * FROM $table limit 1");
122	my $row = $handle->fetchrow_hashref;
123	map { print "<tr><td>$_</td><td><input type=checkbox value=1 name=column_" . displaytable::to_unique_key($_) . "></td></tr>\n"; } keys(%$row);
124	print "</table>\n";
125
126	print "</td></tr></table>\n";
127
128	print "<br>Graph as a Rate: <input type=checkbox value=1 name=graph_as_rate><br>\n";
129	print "<br>Maximum Y Value: <input type=text value=inf name=max_y><br>\n";
130	print "<br>Minimum Y Value: <input type=text value=-inf name=min_y><br>\n";
131
132	print "<input type=hidden name=table value=\"$table\">\n";
133	print "<input type=hidden name=host value=\"$host\">\n";
134	print "<input type=hidden name=dograph value=1>\n";
135	print "<input type=hidden name=group value=\"$group\">\n";
136	print "<input type=submit name=\"Make Graph\">\n";
137
138	print "</form>\n";
139
140	my $handle = getcursor($dbh, "SELECT distinct(oidindex) FROM $table where host = '$host' order by oidindex");
141	return Exit($dbh, $group);
142    }
143    if (param('graph_all_data')) {
144	$clause = "host = '$host'";
145    } else {
146	my $handle = getcursor($dbh, "SELECT distinct(oidindex) FROM $table where host = '$host'");
147	$clause = "where (";
148	while (  $row = $handle->fetchrow_hashref ) {
149#	    print STDERR "graph test: " . $row->{'oidindex'} . "=" . "graph_" . displaytable::to_unique_key($row->{'oidindex'}) . "=" . param("graph_" . displaytable::to_unique_key($row->{'oidindex'})) . "\n";
150	    if (param("graph_" . displaytable::to_unique_key($row->{'oidindex'}))) {
151		$clause .= " or oidindex = " . $row->{'oidindex'} . "";
152	    }
153	}
154
155	my $handle = getcursor($dbh, "SELECT * FROM $table limit 1");
156	my $row = $handle->fetchrow_hashref;
157	map { push @columns, $_ if (param('column_' . displaytable::to_unique_key($_))) } keys(%$row);
158
159	$clause .= ")";
160	$clause =~ s/\( or /\(/;
161	if ($clause =~ /\(\)/ || $#columns == -1) {
162	    $r->content_type("text/html");
163	    $r->send_http_header();
164	    print "<body bgcolor=\"#ffffff\">\n";
165	    print "<h1>No Data to Graph</h1>\n";
166	    print STDERR "No data to graph: $clause, $#columns\n";
167	    return Exit($dbh, "$group");
168	}
169	$clause .= " and host = '$host'";
170    }
171
172#    print STDERR "graphing clause: $clause\n";
173
174    # all is ok, display the graph
175
176    $r->content_type("image/png");
177    $r->send_http_header();
178
179    print STDERR "graphing clause: $clause, columns: ", join(", ",@columns), "\n";
180    my @args;
181    push (@args, '-rate', '60') if (param('graph_as_rate'));
182    push (@args, '-max', param('max_y')) if (param('max_y') && param('max_y') =~ /^[-.\d]+$/);
183    push (@args, '-min', param('min_y')) if (param('min_y') && param('min_y') =~ /^[-.\d]+$/);
184
185    my $ret =
186    displaygraph($dbh, $table,
187#		 '-xcol', "date_format(updated,'%m-%d-%y %h:%i')",
188		 '-xcol', "unix_timestamp(updated)",
189		 '-pngparms', [
190		     'x_labels_vertical', '1',
191		     'x_tick_number', 6,
192		     'x_number_format', \&date_format,
193		     'y_label', 'Count/Min',
194		     'title', $table,
195#		     'y_min_value', 0,
196		 ],
197		 '-clauses', "$clause order by updated",
198		 @args,
199		 '-columns', \@columns,
200		 '-indexes', ['oidindex']);
201    print STDERR "$ret rows graphed\n";
202    return OK();
203}
204
205#===========================================================================
206# Start HTML.
207#===========================================================================
208$r->content_type("text/html");
209$r->send_http_header();
210print "<body bgcolor=\"#ffffff\">\n";
211print "<h1>UCD-SNMP Management Console</h1>\n";
212print "<hr>\n";
213
214#===========================================================================
215# Display mib related data information
216#===========================================================================
217if (param('displayinfo')) {
218    makemibtable(param('displayinfo'));
219    return Exit($dbh, "");
220}
221
222#===========================================================================
223# Display a generic sql table of any kind (debugging).
224#===========================================================================
225# if (my $disptable = param('displaytable')) {
226#     if (param('editable') == 1) {
227# 	print "<form submit=dont>\n";
228# 	displaytable($disptable, -editable, 1);
229# 	print "</form>\n";
230#     } else {
231# 	displaytable($disptable);
232#     }
233#     return Exit($dbh,  "");
234# }
235
236#===========================================================================
237# Get host and group from CGI query.
238#===========================================================================
239my $host = param('host');
240my $group = param('group');
241
242#===========================================================================
243# Editable user information
244#===========================================================================
245
246if (param('setuponcall')) {
247    print "<title>oncall schedule for user: $remuser</title>\n";
248    print "<h2>oncall schedule for user: $remuser</h2>\n";
249    print "<p>Please select your oncall schedule and mailing addresses for your groups below:";
250    if (!isexpert($remuser)) {
251	print "<ul>\n";
252        print "<li>Values for the days/hours fields can be comma seperated lists of hours/days/ranges.  EG: hours: 7-18,0-4.\n";
253	print "</ul>\n";
254    }
255    print "<form method=post><input type=hidden name=setuponcall value=1>\n";
256    displaytable($dbh, 'oncall',
257    '-clauses',"where user = '$remuser' order by groupname",
258    '-select','id, user, groupname, email, pager, days, hours',
259    '-selectorder', 1,
260    '-notitle', 1,
261    '-editable', 1,
262    '-indexes', ['id','user','groupname'],
263    '-CGI', $CGI::Q
264    );
265    print "<input type=submit value=\"submit changes\">\n";
266    print "</form>\n";
267    return Exit($dbh, $group);
268}
269
270#===========================================================================
271# show the list of groups a user belongs to.
272#===========================================================================
273if (!defined($group)) {
274    my @groups = getgroupsforuser($dbh, $remuser);
275    print "<title>Net-SNMP Group List</title>\n";
276    print "<h2>Host groupings you may access:</h2>\n";
277    if (!isexpert($remuser)) {
278	print "<ul>\n";
279	print "<li>Click on a group to operate or view the hosts in that group.\n";
280	print "<li>Click on a red status light below to list the problems found.\n";
281	print "</ul>\n";
282    }
283
284    if ($#groups > 0) {
285	displaytable($dbh, 'usergroups',
286		     '-clauses', "where (user = '$remuser')",
287		     '-select', 'distinct groupname',
288		     '-notitle', 1,
289		     '-printonly', ['groupname'],
290		     '-datalink', sub { my $q = self_url();
291					my $key = shift;
292					my $h = shift;
293					return if ($key ne "groupname");
294					return addtoken($q,"group=$h");
295				    },
296		     '-beginhook',
297		     sub {
298			 my $q = self_url();
299			 my($dbh, $junk, $data) = @_;
300			 if (!defined($data)) {
301			     print "<th>Status</th>";
302			     return;
303			 }
304			 my ($cur, $row);
305			 $cur = getcursor($dbh, "select host from hostgroups where groupname = '$data->{groupname}'");
306			 while (  $row = $cur->fetchrow_hashref ) {
307			     if (checkhost($dbh, $data->{'groupname'},
308					   $row->{'host'})) {
309				 print "<td><a href=\"" . addtoken($q,"group=$data->{groupname}&summarizegroup=1") . "\"><img border=0 src=$NetSNMP::manager::redimage></a></td>\n";
310				 return;
311			     }
312			 }
313			 print "<td><img src=$NetSNMP::manager::greenimage></td>\n";
314		     }
315		     );
316	$dbh->disconnect();
317	return Exit($dbh,  $group);
318    } else {
319	if ($#groups == -1) {
320	    print "You are not configured to use the Net-SNMP-manager, please contact your system administrator.";
321	    return Exit($dbh,  $group);
322	}
323	$group = $groups[0];
324    }
325}
326
327#===========================================================================
328# reject un-authorized people accessing a certain group
329#===========================================================================
330if (!isuser($dbh, $remuser, $group)) {
331    print "Unauthorized access to that group ($group)\n";
332    return Exit($dbh, $group);
333}
334
335#===========================================================================
336# add a new host to a group
337#===========================================================================
338if (defined(my $newhost = param('newhost'))) {
339    if (isadmin($dbh, $remuser, $group)) {
340	if ($dbh->do("select * from hostgroups where host = '$newhost' and groupname = '$group'") eq "0E0") {
341	    $dbh->do("insert into hostgroups(host,groupname) values('$newhost','$group')") ;
342	} else {
343	    print "<b>ERROR: host $newhost already in $group</b>\n";
344	}
345	CGI::delete('newhost');
346    }
347}
348
349#===========================================================================
350# display setup configuration for a group
351#===========================================================================
352if (defined(param('setupgroup'))) {
353    if (isadmin($dbh, $remuser, $group)) {
354	setupgroup($dbh, $group);
355    } else {
356	print "<h2>You're not able to perform setup operations for group $group\n";
357    }
358    return Exit($dbh, $group);
359}
360
361#===========================================================================
362# save configuration information submitted about a group
363#===========================================================================
364if (defined(param('setupgroupsubmit')) &&
365    isadmin($dbh, $remuser, $group)) {
366    setupgroupsubmit($dbh, $group);
367    delete_all();
368    param(-name => 'group', -value => $group);
369    print "<a href=\"" . self_url() . "\">Entries submitted</a>";
370    return Exit($dbh, $group);
371}
372
373#===========================================================================
374# user preferences
375#===========================================================================
376if (defined(param('userprefs'))) {
377    setupuserpreferences($dbh, $remuser, $group);
378    return Exit($dbh, $group);
379}
380
381#===========================================================================
382# save submitted user preferences
383#===========================================================================
384if (defined(param('setupuserprefssubmit')) &&
385    isadmin($dbh, $remuser, $group)) {
386    setupusersubmit($dbh, $remuser, $group);
387    delete_all();
388    param(-name => 'group', -value => $group);
389    print "<a href=\"" . self_url() . "\">Entries submitted</a>";
390    return Exit($dbh, $group);
391}
392
393#===========================================================================
394# summarize problems in a group
395#===========================================================================
396if (defined(param('summarizegroup'))) {
397    print "<title>group problem summary: $group</title>\n";
398    print "<h2>The following is a list of problems in the group \"$group\":</h2>\n";
399    summarizeerrors($dbh, "where groupname = '$group'");
400    return Exit($dbh, $group);
401}
402
403#===========================================================================
404# summarize problems on a host
405#===========================================================================
406if (defined($host) && defined(param('summarizehost'))) {
407    print "<title>host summary: $host</title>\n";
408    print "<h2>The following is a list of problems for the host \"$host\":</h2>\n";
409    summarizeerrors($dbh, "where groupname = '$group' and host = '$host'");
410    return Exit($dbh, $group);
411}
412
413#===========================================================================
414# display a list of hosts in a group
415#===========================================================================
416if (!defined($host)) {
417    print "<title>Net-SNMP Host $host</title>\n";
418    print "<h2>Hosts in the group \"$group\":</h2>\n";
419    if (!isexpert($remuser)) {
420	print "<ul>\n";
421	if (isadmin($dbh, $remuser, $group)) {
422	    my $q = self_url();
423	    $q =~ s/\?.*//;
424            print "<li>Make sure you <a href=\"" . addtoken($q,"group=$group&setupgroup=1") . "\">set up the host</a> for the SNMP tables you want to monitor.\n";
425        }
426	print "<li>Click on a hostname to operate on or view the information tables associated with that group.\n";
427	print "<li>Click on a red status light below to list the problems found in with a particular host.\n";
428	print "</ul>\n";
429    }
430    displaytable($dbh, 'hostgroups',
431		 '-notitle',0,
432		 '-clauses', "where (groupname = '$group')",
433		 '-select', 'distinct host, sysObjectId, sysDescr, sysUpTime, versionTag',
434		 '-datalink', sub { my $q = self_url();
435				    my $key = shift;
436				    my $h = shift;
437				    return if ($key ne "host");
438				    return addtoken($q,"host=$h");
439				},
440		 '-beginhook',
441		 sub {
442		     my $q = self_url();
443		     my($dbh, $junk, $data) = @_;
444		     if (!defined($data)) {
445			 print "<th>Status</th>";
446			 return;
447		     }
448		     if (checkhost($dbh, $group, $data->{'host'})) {
449			 print "<td><a href=\"" . addtoken($q,"group=$group&summarizehost=1&host=$data->{host}") . "\"><img border=0 src=$NetSNMP::manager::redimage></a></td>\n";
450		     } else {
451			 print "<td><img src=$NetSNMP::manager::greenimage></td>\n";
452		     }
453		 }
454		 );
455    if (isadmin($dbh, $remuser, $group)) {
456	addhostentryform($group);
457	my $q = self_url();
458	$q =~ s/\?.*//;
459	print "<a href=\"" . addtoken($q,"group=$group&setupgroup=1") . "\">setup group $group</a>\n";
460    }
461    return Exit($dbh, $group);
462}
463
464#===========================================================================
465# setup the host's history records
466#===========================================================================
467if (param('setuphost')) {
468    print "<title>Net-SNMP history setup for host: $host</title>\n";
469    print "<h2>Net-SNMP history setup for the host: \"$host\"</h2>\n";
470    print "<p>Enter the number of days to keep the data for a given table for the host \"$host\":\n";
471    if (!isexpert($remuser)) {
472	print "<ul>\n";
473        print "<li>Numbers must be greater than or equal to 1 to enable history logging.\n";
474	print "</ul>\n";
475    }
476    print "<form method=post><input type=hidden name=setuphost value=1><input type=hidden name=host value=\"$host\"><input type=hidden name=group value=\"$group\">\n";
477    displaytable($dbh, 'hosttables',
478    '-clauses',"where host = '$host' and groupname = '$group'",
479    '-select','groupname, host, tablename, keephistory',
480    '-selectorder', 1,
481    '-notitle', 1,
482    '-editable', 1,
483    '-indexes', ['groupname','host','tablename'],
484    '-CGI', $CGI::Q
485    );
486    print "<input type=submit value=\"submit changes\">\n";
487    print "</form>\n";
488    return Exit($dbh, $group);
489}
490
491#===========================================================================
492# display a huge table of history about something
493#===========================================================================
494if (param('displayhistory')) {
495    if (!isuser($dbh, $remuser, $group)) {
496        print "Unauthorized access to that group ($group)\n";
497        return Exit($dbh, $group);
498    }
499    displaytable($dbh, param('table'),
500    '-clauses', "where (host = '$host')",
501    '-dolink', \&linktodisplayinfo,
502    '-dontdisplaycol', "select * from userprefs where user = '$remuser' and groupname = '$group' and tablename = ? and columnname = ? and displayit = 'N'"
503    );
504    return Exit($dbh, $group);
505}
506
507#===========================================================================
508# display inforamation about a host
509#  optionally add new collection tables
510#===========================================================================
511showhost($dbh, $host, $group, $remuser);
512if (isadmin($dbh, $remuser, $group)) {
513    if (param('newtables')) {
514    	my $x = param('newtables');
515    	$x =~ s/,/ /g;
516    	if (/[^\w\s]/) {
517    	    print "<br>Illegal table names in addition list: $x<br>\n"
518    	} else {
519	    my @x = split(/\s+/,$x);
520	    foreach my $i (@x) {
521		$dbh->do("insert into hosttables(host, groupname, tablename, keephistory) values('$host','$group','$i','0')");
522	    }
523    	    print "<br>adding: ",join(", ",@x),"<br>\n";
524    	}
525    } else {
526        print "<br>Add new MIB Tables or Groups that you want to collect for this host: <form><input type=hidden name=host value=\"$host\"><input type=hidden name=group value=\"$group\"><input name=\"newtables\" type=text><br><input type=submit value=\"add tables\"></form>\n";
527    }
528    my $q = self_url();
529    $q =~ s/\?.*//;
530    print "<a href=\"" . addtoken($q, "setuphost=1&host=$host&group=$group") . "\">setup host $host</a>\n";
531}
532return Exit($dbh, $group);
533
534#===========================================================================
535# END of handler
536#===========================================================================
537
538}
539
540# add a token to a url string.  Use either a ? or an & depending on
541# existence of ?.
542sub addtoken {
543    my $url = shift;
544    my $token = shift;
545    return "$url&$token" if ($url =~ /\?/);
546    return "$url?$token";
547}
548
549#
550# summarizeerrors(DB-HANDLE, CLAUSE):
551#   summarize the list of errors in a given CLAUSE
552#
553sub summarizeerrors {
554    my $dbh = shift;
555    my $clause = shift;
556    $clause = "where" if ($clause eq "");
557    my $clause2 = $clause;
558    $clause2 =~ s/ host / hosterrors.host /;
559
560    # Major errors
561    displaytable($dbh, 'hosterrors, hostgroups',  # , hostgroups
562		 '-select', "hosterrors.host as host, errormsg",
563		 '-notitle', 1,
564		 '-title', "Fatal Errors",
565		 '-clauses', "$clause2 and hosterrors.host = hostgroups.host",
566		 '-beginhook', sub {
567		     if ($#_ < 2) {
568			 #doing header;
569			 print "<td></td>";
570		     } else {
571			 print "<td><img src=\"$NetSNMP::manager::redimage\"></td>\n";
572		     }});
573
574    my $tabletop = "<br><table $NetSNMP::manager::tableparms><tr $NetSNMP::manager::headerparms><th><b>Host</b></th><th><b>Table</b></th><th><b>Description</b></th></tr>\n";
575    my $donetop = 0;
576    my $cursor =
577	getcursor($dbh, "SELECT * FROM hosttables $clause");
578
579    while (my $row = $cursor->fetchrow_hashref ) {
580
581	my $exprs = getcursor($dbh, "SELECT * FROM errorexpressions where (tablename = '$row->{tablename}')");
582
583	while (my  $expr = $exprs->fetchrow_hashref ) {
584	    my $errors = getcursor($dbh, "select * from $row->{tablename} where $expr->{expression} and host = '$row->{host}'");
585	    while (my  $error = $errors->fetchrow_hashref ) {
586		print $tabletop if ($donetop++ == 0);
587		print "<tr><td>$row->{host}</td><td>$row->{tablename}</td><td>$expr->{returnfield}: $error->{$expr->{returnfield}}</td></tr>";
588	    }
589	}
590    }
591    print "</table>";
592}
593
594#
595# getcursor(CMD):
596#    genericlly get a cursor for a given sql command, displaying and
597#    printing errors where necessary.
598#
599sub getcursor {
600    my $dbh = shift;
601    my $cmd = shift;
602    my $cursor;
603    ( $cursor = $dbh->prepare( $cmd ))
604	or print "\nnot ok: $DBI::errstr\n";
605    ( $cursor->execute )
606	or print( "\tnot ok: $DBI::errstr\n" );
607    return $cursor;
608}
609
610#
611# mykeysort($a, $b)
612#    sorts $a and $b against the order in the mib or against the hard
613#    coded special list.
614#
615sub mykeysort {
616    my $a = $displaytable::a;
617    my $b = $displaytable::b;
618    my $mb = $SNMP::MIB{SNMP::translateObj($b)};
619    my $ma = $SNMP::MIB{SNMP::translateObj($a)};
620
621    return $NetSNMP::manager::myorder{$a} <=> $NetSNMP::manager::myorder{$b} if ((defined($NetSNMP::manager::myorder{$a}) || !defined($ma->{'subID'})) && (defined($NetSNMP::manager::myorder{$b}) || !defined($mb->{'subID'})));
622    return 1 if (defined($NetSNMP::manager::myorder{$b}) || !defined($mb->{'subID'}));
623    return -1 if (defined($NetSNMP::manager::myorder{$a}) || !defined($ma->{'subID'}));
624
625    $ma->{'subID'} <=> $mb->{'subID'};
626}
627
628#
629# checkhost(GROUP, HOST):
630#    if anything in a host is an error, as defined by the
631#    errorexpressions table, return 1, else 0
632#
633sub checkhost {
634    my $dbh = shift;
635    my $group = shift;
636    my $host = shift;
637    my ($tblh);
638
639    return 2 if ($dbh->do("select * from hosterrors where host = '$host'") ne "0E0");
640
641    # get a list of tables we want to display
642    $tblh = getcursor($dbh, "SELECT * FROM hosttables where (host = '$host' and groupname = '$group')");
643
644    # table data
645    my($exprs, $tablelist);
646    while ( $tablelist = $tblh->fetchrow_hashref ) {
647	$exprs = getcursor($dbh, "SELECT * FROM errorexpressions where (tablename = '$tablelist->{tablename}')");
648	while(my $expr = $exprs->fetchrow_hashref) {
649	    if ($dbh->do("select * from $tablelist->{tablename} where $expr->{expression} and host = '$host'") ne "0E0") {
650		return 1;
651	    }
652	}
653    }
654    return 0;
655}
656
657#
658#  showhost(HOST):
659#
660#    display all the tables monitored for a given host (in a group).
661#
662sub showhost {
663    my $dbh = shift;
664    my $host = shift;
665    my $group = shift;
666    my $remuser = shift;
667    my $q = self_url();
668    $q =~ s/\?.*//;
669    # host header
670    print "<title>Net-SNMP manager report for host: $host</title>\n";
671    print "<h2>Monitored information for the host $host</h2>\n";
672    if (!isexpert($remuser)) {
673	print "<ul>\n";
674	print "<li>Click on a column name for information about the data in that column.\n";
675	print "<li>Click on a column name or table name for information about the data in the table.\n";
676	print "<li>If you are <a href=\"" . addtoken($q, "setuphost=1&host=$host&group=$group") . "\">collecting past history</a> for a data set, links will appear below the table that allow you to view and/or graph the historic data.\n";
677	print "</ul>\n";
678    }
679
680    # does the host have a serious error?
681
682    my $errlist = getcursor($dbh, "SELECT * FROM hosterrors where (host = '$host')");
683    if ( $dbh->do("SELECT * FROM hosterrors where (host = '$host')") ne "0E0") {
684	displaytable($dbh, 'hosterrors',
685		     '-clauses', "where (host = '$host')",
686		     '-dontdisplaycol', "select * from userprefs where user = '$remuser' and groupname = '$group' and tablename = ? and columnname = ? and displayit = 'N'",
687		     '-beginhook', sub {
688			 if ($#_ < 2) {
689			     #doing header;
690			     print "<td></td>";
691			 } else {
692			     print "<td><img src=\"$NetSNMP::manager::redimage\"></td>\n";
693			 }});
694    }
695
696    # get a list of tables we want to display
697    my $tblh = getcursor($dbh, "SELECT * FROM hosttables where (host = '$host' and groupname = '$group')");
698
699    # table data
700    my($tablelist);
701    while (  $tablelist = $tblh->fetchrow_hashref ) {
702
703	displaytable($dbh, $tablelist->{'tablename'},
704		     '-clauses', "where (host = '$host') order by oidindex",
705		     '-dontdisplaycol', "select * from userprefs where user = '$remuser' and groupname = '$group' and tablename = ? and columnname = ? and displayit = 'N'",
706		     '-sort', \&mykeysort,
707		     '-dolink', \&linktodisplayinfo,
708		     '-beginhook', \&printredgreen);
709	if ($tablelist->{'keephistory'}) {
710	    my $q = self_url();
711	    $q =~ s/\?.*//;
712	    print "history: ";
713	    print "<a href=\"" . addtoken($q, "displayhistory=1&host=$host&group=$group&table=$tablelist->{'tablename'}hist") . "\">[table]</a>\n";
714	    print "<a href=\"" . addtoken($q, "displaygraph=1&host=$host&group=$group&table=$tablelist->{'tablename'}hist") . "\">[graph]</a>\n";
715	    print "<br>\n";
716	}
717    }
718}
719
720#
721#  linktodisplayinfo(STRING):
722#
723#    returns a url to the appropriate displayinfo link if STRING is a
724#    mib node.
725#
726sub linktodisplayinfo {
727    return if (exists($NetSNMP::manager::myorder{shift}));
728    return self_url() . "&displayinfo=" . shift;
729}
730
731# printredgreen(TABLENAME, DATA):
732#
733#   display a red or a green dot in a table dependent on the table's
734#   values and associated expression
735#
736#   DATA is NULL when in a header row (displaying header names).
737#
738sub printredgreen {
739    my $dbh = shift;
740    my $tablename = shift;
741    my $data = shift;
742    my ($exprs, $expr, $img);
743
744    if (!defined($data)) {
745	#doing header;
746	print "<td></td>";
747	return;
748    }
749
750    my $cmd = "SELECT * FROM errorexpressions where (tablename = '$tablename')";
751    print " $cmd\n" if ($NetSNMP::manager::verbose);
752    ( $exprs = $dbh->prepare( $cmd ) )
753	or die "\nnot ok: $DBI::errstr\n";
754    ( $exprs->execute )
755	or print( "\tnot ok: $DBI::errstr\n" );
756
757    $img = $NetSNMP::manager::greenimage;
758    while($expr = $exprs->fetchrow_hashref) {
759	if ($dbh->do("select oidindex from $tablename where host = '$data->{host}' and oidindex = '$data->{oidindex}' and $expr->{expression}") ne "0E0") {
760	    $img = $NetSNMP::manager::redimage;
761	}
762    }
763    print "<td><img src=$img></td>";
764}
765
766#
767# display information about a given mib node as a table.
768#
769sub makemibtable {
770    my $dispinfo = shift;
771    # display information about a data type in a table
772    my $mib = $SNMP::MIB{SNMP::translateObj($dispinfo)};
773    print "<table $NetSNMP::manager::tableparms><tr><td>\n";
774    foreach my $i (qw(label type access status units hint moduleID description enums)) {
775#    foreach my $i (keys(%$mib)) {
776	next if (!defined($$mib{$i}) || $$mib{$i} eq "");
777	next if (ref($$mib{$i}) eq "HASH" && $#{keys(%{$$mib{$i}})} == -1);
778	print "<tr><td>$i</td><td>";
779	if (ref($$mib{$i}) eq "HASH") {
780	    print "<table $NetSNMP::manager::tableparms><tr><td>\n";
781	    foreach my $j (sort { $$mib{$i}{$a} <=> $$mib{$i}{$b} } keys(%{$$mib{$i}})) {
782 		print "<tr><td>$$mib{$i}{$j}</td><td>$j</td></tr>";
783	    }
784	    print "</table>\n";
785	} else {
786	    print "$$mib{$i}";
787	}
788	print "</td></tr>\n";
789    }
790    print "</table>\n";
791}
792
793# given a user, get all the groups he belongs to.
794sub getgroupsforuser {
795    my (@ret, $cursor, $row);
796    my ($dbh, $remuser) = @_;
797    ( $cursor = $dbh->prepare( "SELECT * FROM usergroups where (user = '$remuser')"))
798	or die "\nnot ok: $DBI::errstr\n";
799    ( $cursor->execute )
800	or print( "\tnot ok: $DBI::errstr\n" );
801
802    while (  $row = $cursor->fetchrow_hashref ) {
803	push(@ret, $row->{'groupname'});
804    }
805    @ret;
806}
807
808# given a host, get all the groups it belongs to.
809sub gethostsforgroup {
810    my (@ret, $cursor, $row);
811    my ($dbh, $group) = @_;
812    ( $cursor = $dbh->prepare( "SELECT * FROM hostgroups where (groupname = '$group')"))
813	or die "\nnot ok: $DBI::errstr\n";
814    ( $cursor->execute )
815	or print( "\tnot ok: $DBI::errstr\n" );
816
817    while (  $row = $cursor->fetchrow_hashref ) {
818	push(@ret, $row->{'host'});
819    }
820    @ret;
821}
822
823# display the host add entry box
824sub addhostentryform {
825    my $group = shift;
826    print "<form method=\"get\" action=\"" . self_url() . "\">\n";
827    print "Add a new host to the group \"$group\": <input type=\"text\" name=\"newhost\"><br>";
828    print "<input type=\"hidden\" name=\"group\" value=\"$group\">";
829    print "<input type=submit value=\"Add Hosts\">\n";
830    print "</form>";
831}
832
833#is an expert user?
834sub isexpert {
835    return 0;
836}
837
838#is remuser a admin?
839sub isadmin {
840    my ($dbh, $remuser, $group) = @_;
841    return 0 if (!defined($remuser) || !defined($group));
842    return 1 if ($dbh->do("select * from usergroups where user = '$remuser' and groupname = '$group' and isadmin = 'Y'") ne "0E0");
843    return 0;
844}
845
846#is user a member of this group?
847sub isuser {
848    my ($dbh, $remuser, $group) = @_;
849    return 0 if (!defined($remuser) || !defined($group));
850    return 1 if ($dbh->do("select * from usergroups where user = '$remuser' and groupname = '$group'") ne "0E0");
851    return 0;
852}
853
854# displayconfigarray(HOSTS, NAMES, CONFIG):
855#
856#   displays an array of generic check buttons to turn on/off certain
857#   variables.
858sub displayconfigarray {
859    my $dbh = shift;
860    my $hosts = shift;
861    my $names = shift;
862    my %config = @_;
863
864    my $cmd;
865    if ($config{'-check'}) {
866	( $cmd = $dbh->prepare( $config{'-check'} ) )
867	    or die "\nnot ok: $DBI::errstr\n";
868    }
869
870    print "<table $NetSNMP::manager::tableparms>\n";
871    print "<tr><td></td>";
872    my ($i, $j);
873    foreach $j (@$names) {
874	my $nj = $j;
875	$nj = $j->[0] if ($config{'-arrayrefs'} || $config{'-arrayref2'});
876	print "<td>$nj</td>";
877    }
878    foreach my $i (@$hosts) {
879	my $ni = $i;
880	$ni = $i->[0] if ($config{'-arrayrefs'} || $config{'-arrayref1'});
881	print "<tr><td>$ni</td>";
882	foreach $j (@$names) {
883	    my $nj = $j;
884	    $nj = $j->[0] if ($config{'-arrayrefs'} || $config{'-arrayref2'});
885	    my $checked = "checked" if (defined($cmd) && $cmd->execute($ni,$nj) ne "0E0");
886	    print "<td><input type=checkbox $checked value=y name=" . $config{prefix} . $ni . $nj . "></td>\n";
887	}
888	print "</tr>\n";
889    }
890    print "</tr>";
891    print "</table>";
892}
893
894sub adddefaulttables {
895    my ($dbh, $names) = @_;
896    my $row;
897    # add in known expression tables.
898    my $handle = getcursor($dbh, "SELECT * FROM errorexpressions");
899
900    expr:
901    while($row = $handle->fetchrow_hashref) {
902	foreach $i (@$names) {
903	    if ($i->[0] eq $row->{tablename}) {
904		next expr;
905	    }
906	}
907	push @$names, [$row->{tablename}];
908    }
909}
910
911#
912# display the setup information page for a given group.
913#
914sub setupgroup {
915    my $dbh = shift;
916    my $group = shift;
917
918    my ($hosts, $names) = gethostandgroups($dbh, $group);
919    adddefaulttables($dbh, $names);
920
921    print "<form method=\"post\" action=\"" . self_url() . "\">\n";
922    print "<input type=hidden text=\"setupgroupsubmit\" value=\"y\">";
923    displayconfigarray($dbh, $hosts, $names,
924		       -arrayrefs, 1,
925		       -check, "select * from hosttables where (host = ? and tablename = ? and groupname = '$group')");
926    print "<input type=hidden name=group value=\"$group\">\n";
927    print "<input type=submit value=submit name=\"setupgroupsubmit\">\n";
928    print "</form>";
929}
930
931# a wrapper around fetching arrays of everything in a table.
932sub getarrays {
933    my $dbh = shift;
934    my $table = shift;
935    my %config = @_;
936    my $selectwhat = $config{'-select'} || "*";
937    my $handle;
938
939    $handle = getcursor($dbh, "SELECT $selectwhat FROM $table $config{-clauses}");
940    return $handle->fetchall_arrayref;
941}
942
943#
944# get a list of all tablenames and hostnames for a given group.
945#
946sub gethostandgroups {
947    my $dbh = shift;
948    my $group = shift;
949    my ($tbnms);
950
951    my $names = getarrays($dbh, 'hosttables',
952			  "-select", 'distinct tablename',
953			  "-clauses", "where groupname = '$group'");
954
955    my $hosts = getarrays($dbh, 'hostgroups',
956			  "-select", 'distinct host',
957			  "-clauses", "where groupname = '$group'");
958
959    return ($hosts, $names);
960}
961
962sub setupgroupsubmit {
963    my $dbh = shift;
964    my $group = shift;
965
966    my ($hosts, $names) = gethostandgroups($dbh, $group);
967    adddefaulttables($dbh, $names);
968
969    foreach my $i (@$hosts) {
970	$dbh->do("delete from hosttables where host = '${$i}[0]' and groupname = '$group'");
971    }
972    my $rep = $dbh->prepare("insert into hosttables(host,tablename,groupname) values(?,?,'$group')");
973
974    foreach my $i (@$hosts) {
975	foreach my $j (@$names) {
976	    if (param("${$i}[0]" . "${$j}[0]")) {
977		print "test: ","${$i}[0] : ${$j}[0]<br>\n";
978		$rep->execute("${$i}[0]", "${$j}[0]") || print "$! $DBI::errstr<br>\n";
979            }
980	}
981    }
982
983}
984
985#
986# save user pref data submitted by the user
987#
988sub setupusersubmit {
989    my ($dbh, $remuser, $group) = @_;
990    my $tables = getarrays($dbh, 'hosttables',
991			   "-select", 'distinct tablename',
992			   "-clauses", "where groupname = '$group'");
993
994    $dbh->do("delete from userprefs where user = '$remuser' and groupname = '$group'");
995    my $rep = $dbh->prepare("insert into userprefs(user, groupname, tablename, columnname, displayit) values('$remuser', '$group', ?, ?, 'N')");
996
997    my ($i, $j);
998    foreach my $i (@$tables) {
999	my $sth = $dbh->prepare("select * from ${$i}[0] where 1 = 0");
1000	$sth->execute();
1001
1002	foreach $j (@{$sth->{NAME}}) {
1003	    if (param("${$i}[0]" . "$j")) {
1004		$rep->execute("${$i}[0]", "$j");
1005	    }
1006	}
1007    }
1008}
1009
1010sub Exit {
1011    my ($dbh, $group) = @_;
1012    my $tq = self_url();
1013    $tq =~ s/\?.*//;
1014    print "<hr>\n";
1015    print "<a href=\"$tq\">[TOP]</a>\n";
1016    print "<a href=\"$tq?userprefs=1&group=$group\">[display options]</a>\n";
1017    print "<a href=\"$tq?setuponcall=1\">[setup oncall schedule]</a>\n";
1018    if (defined($group)) {
1019	print "<a href=\"$tq?group=$group\">[group: $group]</a>\n";
1020	print "<a href=\"$tq?group=$group&summarizegroup=1\">[summarize errors]</a>\n";
1021    }
1022    $dbh->disconnect() if (defined($dbh));
1023    return OK();
1024#    exit shift;
1025}
1026
1027#
1028# setup user preferences by displaying a configuration array of
1029# checkbuttons for each table.
1030#
1031sub setupuserpreferences {
1032    my ($dbh, $remuser, $group) = @_;
1033    my $tables = getarrays($dbh, 'hosttables',
1034			   "-select", 'distinct tablename',
1035			   "-clauses", "where groupname = '$group'");
1036
1037    print "<h3>Select the columns from the tables that you want to <b>hide</b> below and click on submit:</h3>\n";
1038    print "<form method=\"post\" action=\"" . self_url() . "\">\n";
1039
1040    my ($i, $j);
1041    foreach my $i (@$tables) {
1042	my $sth = $dbh->prepare("select * from ${$i}[0] where 1 = 0");
1043	$sth->execute();
1044	displayconfigarray($dbh, [${$i}[0]], $sth->{NAME},
1045			   -check, "select * from userprefs where (tablename = ? and columnname = ? and user = '$remuser' and groupname = '$group' and displayit = 'N')");
1046    print "<br>\n";
1047    }
1048    print "<input type=hidden name=group value=\"$group\">\n";
1049    print "<input type=submit value=submit name=\"setupuserprefssubmit\">\n";
1050    print "</form>";
1051}
1052