#!/usr/bin/perl -w

# STAMOS 0.5 Some Things About My Own Server

# written and (c) by Knut Grahlmann <Knut.Grahlmann@web.de>

# initial release April/05/1999
# 0.2  April/10/1999
# 0.3  April/11/1999
# 0.4  October/18/1999
# 0.41 July/25/2000
# 0.5  December/29/2001

# this script reads information about the system from various sources and writes
# it into an HTML file
# it heavily depents on the program uptimed by Rob Kaper <cap@capsi.com>;
# you can get it from http://cx.capsi.com/code-uptimed.html
# the latest version to be known to work with STAMOS is 0.1.7

# this script is published under the terms of the GPL,
# see LICENSE for further information


use Getopt::Long;

$STAMOS_version="0.5";
$STAMOS_URL="http://www.theknuddel.de/index_en.html";

$date=`date`; 		# the output of the date command
$number_of_hds=0;

# declare these up here as empty, so that they don't produce
# "Use of uninitialized value" when I test if they are declared
$uptimed_path="";
$output_file="";
$config_file="";
$language_file="";
$hostname="";
$CPU[0]="";
$CPU[1]="";
$CPU[2]="";
$CPU[3]="";
$MEM="";
$own_body="";
$own_start="";
$own_end="";
$own_style="";
$own_table="";
$manufacturer="";
$model="";
$overwrite=0;
$quiet=0;
$verbose=0;
$help=0;
$NFS_occured=0;

# gets commandline parameters
&parameters;

# is the name of a configfile provided? is there one in /etc?
# if so, read it and get the parameters out of it
if ($config_file ne "") {&configfile;}
else {
	if (-e "/etc/stamos.conf") {
		$config_file="/etc/stamos.conf";
		&configfile;
	}
}

# without these two parameters, the program can't work
if ($uptimed_path eq "") {
	&help;
	die "You have to provide the path of the uptimed programs. See README if you have no clue what uptimed is.\n";
}
if ($output_file eq "") {
	&help;
	die "You have to provide the name and the full path of the output file.\n";
}

# and now, the commercial :-)
if ($quiet!=1) {
	print "STAMOS $STAMOS_version  Some Things About My Own Server                    by Knut Grahlmann\n\n";
}

# some users aren't very smart
if ($quiet==1 && $verbose==1) {
	print "You said that I should be quiet and verbose. I guess, I should be quiet after\n";
	print "this message.\n";
	$verbose=0;
}

# I don't want a path looking like /opt/uptimed//uprecords; naw g
if (substr($uptimed_path,length($uptimed_path),1) eq "/") {
	$uprecords=$uptimed_path."uprecords";
}
else {
	$uprecords=$uptimed_path."/uprecords";
}

# finds most of the stuff (/proc-files)
&find_specs;

# a bit more complicated stuff, let's give it its own sub
&disks;

# same for the uptime
&uptime;

# declares default language (english) for the HTML-output
# and optionally reads translations from a file
&language;

# generates the HTML output
&output;

if ($quiet!=1) {
	print "Generated $output_file.\n";
	print "Have a nice day...\n";
}
exit 0;
#-------------------
# end of the program


# reads the configfile
sub configfile {
# list of all valid options
# and don't ask me why it needs to be in alphabetical order!
@option_list=qw(CPU1 CPU2 CPU3 CPU4 hostname HTML_body HTML_end HTML_start HTML_style HTML_table language_file manufacturer model output_file overwrite quiet RAM uptimed_path verbose);

# file present
if (!(-e $config_file)) {
	die "The configuration file doesn't exist.";
}

open (CONFIGFILE,$config_file) || die "Could not open $config_file: $!";

while (<CONFIGFILE>) {
	# we don't need empty lines and such starting with #
	if (substr($_,0,1) eq "#" || length($_)==1) { # empty lines contain \n, so 1
		next;
	}

	# no \n, get everything until the first space and delete spaces
	chomp;
	$position_space=index($_," ");
	$option=substr($_,0,$position_space);
	$option=~ tr/ //d;

	# get the real option value
	$value=substr($_,$position_space+1);

	# checks if empty parameter
	if ($value eq "") { next;}

	# converts yes/no into 1/0
	if ($value eq "yes") {$value=1;}
	if ($value eq "no") {$value=0;}

	$length_array=@option_list;
	
	for ($num_option=0;$num_option<=($length_array-1);++$num_option) {
		if ($option eq $option_list[$num_option]) {
			if ($num_option==0) {$CPU[0]=$value;last;}
			if ($num_option==1) {$CPU[1]=$value;last;}
			if ($num_option==2) {$CPU[2]=$value;last;}
			if ($num_option==3) {$CPU[3]=$value;last;}
			if ($num_option==4) {$hostname=$value;last;}
			if ($num_option==5) {$own_body=$value;last;}
			if ($num_option==6) {$own_end=$value;last;}
			if ($num_option==7) {$own_start=$value;last;}
			if ($num_option==8) {$own_style=$value;last;}
			if ($num_option==9) {$own_table=$value;last;}
			# some options can be overridden with
			# commandline parameters
			if ($num_option==10) {
				if ($language_file eq "") {$language_file=$value;}
				last;
			}
			if ($num_option==11) {$manufacturer=$value;last;}
			if ($num_option==12) {$model=$value;last;}
			if ($num_option==13) {
				if ($output_file eq "") {$output_file=$value;}
				last;
			}
			if ($num_option==14) {
				if ($overwrite==0) {$overwrite=$value;}
				last;
			}
			if ($num_option==15) {
				if ($quiet==0) {$quiet=$value;}
				last;
			}
			if ($num_option==16) {$MEM=$value;last;}
			if ($num_option==17) {
				if ($uptimed_path eq "") {$uptimed_path=$value;}
				last;
			}
			if ($num_option==18) {
				if ($verbose==0) {$verbose=$value;}
				last;
			}
			last;
		} #end if
	} #end for

	# check if option is valid
	if ($num_option==$length_array && $verbose==1 && $quiet!=1) {
		close CONFIGFILE;
		die "Unknown option: $option in $config_file";
	}
} #end while

close CONFIGFILE;
} #end sub configfile


# gets command line parameters
sub parameters {
	&GetOptions("uptimed=s" => \$uptimed_path, "verbose" => \$verbose, "output=s" => \$output_file,"overwrite" =>\$overwrite,"quiet" =>\$quiet,"help" =>\$help,"config=s" =>\$config_file,"language=s" =>\$language_file);
	if ($help==1) {
		&help;
		exit 1;
	}
} #end sub parameters


# prints the help screen
sub help {
	print "STAMOS $STAMOS_version  Some Things About My Own Server                    by Knut Grahlmann\n\n";
	print "STAMOS gathers several information (CPU, RAM, uptime etc.) about the computer it\n";
	print "is running on and generates an HTML file with these information.\n\n";
	print "usage: stamos options \n";
	print "Some options are mandatory, some aren't.\n";
	print "\nmandatory options:\n";
	print "--output\t\tname and full path of the file where STAMOS\n";
	print "\t\t\tshould write to\n";
	print "--uptimed\t\tthe path to the folder with the uprecords program in it\n";
	print "\nnon-mandatory options:\n";
	print "--config\t\tthe path of the configfile, overrides the standard file\n";
	print "\t\t\t/etc/stamos.conf\n";
	print "--help\t\t\tthis screen\n";
	print "--language\t\tspecifies a \"language-file\" (see README)\n";
	print "--overwrite\t\toverwrite an existing file.\n";
	print "--quiet\t\t\tproduce (unless there are errors) NO screen output\n";
	print "--verbose\t\texplain what is being done\n";
	print "\n";
	print "example:\n";
	print "stamos --output=/usr/local/httpd/htdocs/about.html --uptimed=/opt/uptimed --verbose\n";
} #end sub help


# generates the HTML output file
sub output {
	# outputfile already existing?
	if ($overwrite!=1) {
		if (-e $output_file) {
			die "The output file already exists. Please delete it or use the --overwrite option.\n";
		}
	}
	
	# OF=OutputFile
	open (OF,">$output_file");
	
	print OF "<html><head><title>$op_spec $hostname</title>$own_style</head>\n";

	if ($own_body ne "") {print OF "$own_body\n";}
	else {print OF "<body>\n";}
	print OF "<center><h2>$op_spec <b>$hostname</b></h2>\n";
	print OF "<b>$op_asof $date</b><br>\n";
	print OF "$op_curupt: <i>$uptime_days $op_days $uptime_hms</i> (# $place), $op_runsin <i>$start_time</i>\n";

	if ($own_start ne "") {print OF "$own_start\n";}
	else {print OF "<hr>\n";}

	if ($own_table ne "") {print OF "<p><table width=50% $own_table>\n";}
	else {	print OF "<p><table border=1 width=50%>\n";}

	if ($manufacturer ne "") {print OF "<tr width=100%><td width=50%>$op_manu</td><td width=50%>$manufacturer</td></tr>\n";}
	if ($model ne "") {print OF "<tr width=100%><td width=50%>$op_model</td><td width=50%>$model</td></tr>\n";}

	for ($i=0;$i<=$noc;$i++) {
		print OF "<tr width=100%><td width=50%>$op_proc $i</td><td width=50%>$CPU[$i]</td></tr>\n";	
		print OF "<tr width=100%><td width=50%>&#160&#160 -MHz</td><td width=50%>$mhz[$i]</td></tr>\n";
		print OF "<tr width=100%><td width=50%>&#160&#160 -$op_bogomi</td><td width=50%>$bogomips[$i]</td></tr>\n";
	}

	# spacer
	print OF "<th colspan=2> </th>\n";
	
	print OF "<tr width=100%><td width=50%>$op_ram</td><td width=50%>$MEM</td></tr>\n";
	
	# spacer
	print OF "<th colspan=2> </th>\n";
	
	print OF "<tr width=100%><td width=50%>$op_loadav</td><td width=50%>&#160</td></tr>\n";
	print OF "<tr width=100%><td width=50%>&#160&#160 -$op_lamin</td><td width=50%>$loadavg_1</td></tr>\n";
	print OF "<tr width=100%><td width=50%>&#160&#160 -$op_la5min</td><td width=50%>$loadavg_5</td></tr>\n";
	print OF "<tr width=100%><td width=50%>&#160&#160 -$op_la15min</td><td width=50%>$loadavg_15</td></tr>\n";

	#spacer
	print OF "<th colspan=2> </th>\n";
	
	print OF "<tr width=100%><td width=50%>$op_osver</td><td width=50%>$OS $OS_version</td></tr>\n";
	print OF "<tr width=100%><td width=50%>$op_bestup</td><td width=50%>$uptime_days_best $op_days $uptime_hms_best</td></tr>\n";
	print OF "<tr width=100%><td width=50%>&#160&#160 -$op_start</td><td width=50%>$start_time_best</td></tr>\n";

	#spacer
	print OF "<th colspan=2> </th>\n";
	print OF "</table>";

	if ($own_table ne "") {print OF "<table width=50% $own_table>\n";}
	else {print OF "<table width=50% border=1>\n";}

	print OF "<th colspan=6>$op_hds:</th>\n";
	print OF "<tr width=100%><td width=20%>";

	if ($NFS_occured==1) {print OF "$op_dev/<br>$op_mopo";}
	else { print OF "$op_dev";}
	
	print OF "</td><td align=right width=20%>$op_size</td><td align=right width=20%>$op_used</td><td align=right width=20%>$op_free</td><td align=right width=20%>$op_peruse</td><td align=right width=20%>$op_mount</td></tr>\n";
	for ($counter=0;$counter<=($number_of_hds-1);++$counter) {
		print OF "<tr width=100%><td width=20%>$HD_names[$counter]</td><td align=right width=20%>$HD_sizes[$counter]</td><td align=right width=20%>$HD_used[$counter]</td><td align=right width=20%>$HD_free[$counter]</td><td align=right width=20%>$HD_percentage[$counter]</td><td align=right width=20%>$HD_mount[$counter]</td></tr>\n";
	}
	print OF "</table>\n";
	
	print OF "<p>$op_genby <a href=\"$STAMOS_URL\" target=\"_blank\"><b>STAMOS $STAMOS_version</b></a></center>";

	if ($own_end ne "") {print OF "<p>$own_end\n";}	
	print OF "</body></html>";

	close OF;
} #end sub output


# "cat"s df into $_ and extracts, which HDs are used, how big they are how much
# space is used, and how much is free (what is surprisingly not size-used)
sub disks {

open (DISKS,"df|") || die "Could not read from df: $!";

	my $line_counter=-1;
	my $NFS=0;

	WHILE: while (<DISKS>) {

		# the first line is not necessary
		if ($line_counter==-1) {
			++$line_counter;
			next WHILE;
		}
		chomp;

		# floppy disk drives are not the real thrill either
		if (substr($_,0,7) eq "/dev/fd") {
			next WHILE;
		}

		# if you have an NFS-drive, the output of df is like this:
		# IP:/mount-point
		#                size etc.
		if (length($_) < 50) {
			$pos_of_slash=index($_,":/")+1;
			chomp($HD_names[$line_counter]=substr($_,$pos_of_slash));
			$HD_names[$line_counter]=~ tr/ //d;
			$NFS=1;
			$NFS_occured=1;
			next WHILE;
		}

		if ($NFS!=1) {
			$HD_names[$line_counter]=substr($_,0,10);
			$HD_names[$line_counter]=~ tr/ //d;
		}
		else {$NFS=0;}

		# code courtesy of Peter Kovacs <kovacsp@kova.cx>
		($HD_sizes[$line_counter],$HD_used[$line_counter],$HD_free[$line_counter],$HD_percentage[$line_counter],$HD_mount[$line_counter])=($_ =~ /\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\D+\s+(\S+)/);


		if ($verbose==1) {print "$HD_names[$line_counter]\t$HD_sizes[$line_counter]\t$HD_used[$line_counter]\t$HD_free[$line_counter]\t$HD_percentage[$line_counter]\t$HD_mount[$line_counter]\n";}
		
		++$line_counter;
	} #end WHILE
	close DISKS;

	# so that I can make a for-to loop in &output to print all harddrives
	$number_of_hds=$line_counter;
} #end sub disks


# "cat"s several /proc files into $_
# does it need more comments?
sub find_specs {

	# already declared in conf file?
	if ($hostname eq "") {
		$hostname=$ENV{"HOSTNAME"};
		if ($verbose==1) {print "hostname: $hostname\n";}
	}

	# find out the OS and its version
	open (OS,"(cat /proc/version)|") || die "Could not read /proc/version: $!";;
	while (<OS>) {
		($OS,$OS_version)=($_=~ /(\w+)\s+\w+\s+(\d+\D\d+\D\d+)/);
	}
	close OS;
		
	if ($verbose==1) {print "OS: $OS $OS_version\n";}

	# number of CPUs
	$noc=0;

	open (CPU,"(cat /proc/cpuinfo)|") || die "Could not read /proc/cpuinfo: $!";;
	while (<CPU>) {
		chomp;
	
		# Linus changed the /proc-system in some aspects from 2.0 to 2.2 and above:
		# the information that used to be under model, is now under model name
		if (!($OS_version=~/2.0./)) {
			# already declared in conf file?
			if (substr($_,0,10) eq "model name" && $CPU[$noc] eq "") {
				# I don't use regex here, because processor names can
				# consist of one, two or more words, so that substr is
				# easier this time
				$CPU[$noc]=substr($_,index($_,":")+1);
			}
		}
		else {
			# already declared in conf file?
			if (substr($_,0,5) eq "model" && $CPU[$noc] eq "") {
				$CPU[$noc]=substr($_,index($_,":")+1);
			}
		}
		
		if (substr($_,0,8) eq "bogomips") {
			($bogomips[$noc])=($_ =~ /\w+\s+\W\s+(\d+\D\d+)/);
		}

		if (substr($_,0,7) eq "cpu MHz") {
			($mhz[$noc])=($_ =~ /\w+\s+\W\s+(\d+\D\d+)/);
		}

		if (substr($_,0,9) eq "processor") {
			if (substr($_,index($_,":")+1) > 0) {++$noc;}
		}
	
	} #end while
	close CPU;

	if ($verbose==1) {
		for ($i=0;$i<=$noc;$i++) {print "CPU $i: $CPU[$i] with $mhz[$i] MHz and $bogomips[$i] bogomips\n";}
	}

	# already declared in conf file?
	if ($MEM eq "") {
		open (MEM,"(cat /proc/meminfo)|") || die "Could not read /proc/meminfo: $!";;
		while (<MEM>) {
			if (substr($_,0,8) eq "MemTotal") {
				($MEM)=($_=~/\D+\s+(\d+\s+\w+)/);
			}
		}
		close MEM;
	
		if ($verbose==1) {print "RAM: $MEM\n";}
	}

	# load averages can't be found out in a conf file
	open (LOAD,"(cat /proc/loadavg)|") || die "Could not read /proc/loadavg: $!";;
	while (<LOAD>) {
		chomp;
		($loadavg_1,$loadavg_5,$loadavg_15)=($_=~ /(\d+\D\d+)\s+(\d+\D\d+)\s+(\d+\D\d+)/);
	}
	close LOAD;
	
	if ($verbose==1) {print "load averages last 1,5,15 minute(s): $loadavg_1, $loadavg_5, $loadavg_15\n";}
} #end sub find_specs


# "cat"s the output of uprecords into $_and extracts the following values:
# the current uptime (in days and hour:minutes:seconds) and the record
sub uptime {
	open (UPTIME,"$uprecords|") || die "Could not read $uprecords: $!";
	my $line_counter=1;
		
	WHILE: while (<UPTIME>) {
		# the first line in the file is a "header line"; some spaces first
		# The thing is that a regular line starts with spaces, too, so that
		# one can't tell the difference
		if ($line_counter == 1) {
			++$line_counter;
			next WHILE;
		}

		# lines that start with these chracters are not usable
		if ((substr($_,0,1) eq "-") || (substr($_,0,1) eq "n") || (substr($_,0,1) eq "1")) {
			next WHILE;
		}
		
		chomp;
		$actual_line=$_;

		# line 2 is the line with the best uptime
		if ($line_counter==2) {

			# if the current uptime happens to be the best uptime
			if (substr($actual_line,5,1) eq '>') {
				$uptime_days=substr($actual_line,12,4);
				$uptime_days=~ tr/ //d;
				$uptime_days_best=$uptime_days;
				$uptime_hms=substr($actual_line,23,8);
				$uptime_hms_best=$uptime_hms;
				$start_time=substr($actual_line,66,25);
				$start_time_best=$start_time;
				$place=1;
				# exits the while loop because we already found
				# everything
				last WHILE;
			}
			
			# this is just the regular best uptime
			$uptime_days_best=substr($actual_line,8,4);
			$uptime_days_best=~ tr/ //d;
			$uptime_hms_best=substr($actual_line,19,8);
			$start_time_best=substr($actual_line,54,25);

			++$line_counter;
		}

		# the current uptime has an arrow (->) in front of it
		if (substr($actual_line,5,1) eq '>') {
			$place=substr($actual_line,8,2);
			$place=~ tr/ //d;
			# the following case occurs if the current uptime is not
			# even high enough to be in the top ten
			if ($place eq "ow") {
				$place="-";
			}
			$uptime_days=substr($actual_line,12,4);
			$uptime_days=~ tr/ //d;
			$uptime_hms=substr($actual_line,23,8);		
			$start_time=substr($actual_line,66,25);
			last WHILE;
		}
		
	} #end while
	close UPTIME;

	if ($verbose==1) {print "uptime: $uptime_days days $uptime_hms\n\n";}
} #end sub uptime

# declares the default language of the HTML-output: english
# then, a file is read which [hopefully :-)] contains declarations in another
# language
sub language {
	# first, all variables are declared in english, in case the language
	# file is corrupted or not up to date
	# (op=output)
	$op_asof="as of";
	$op_bestup="best uptime";
	$op_bogomi="bogomips";
	$op_curupt="current uptime";
	$op_days="day(s)";
	$op_dev="device";
	$op_free="free (kB)";
	$op_genby="generated by";
	$op_hds="hard drives";
	$op_manu="manufacturer";
	$op_model="model";
	$op_mopo="mount-point";
	$op_osver="OS version";
	$op_peruse="% used";
	$op_proc="processor";
	$op_lamin="last minute";
	$op_la5min="last 5 minutes";
	$op_la15min="last 15 minutes";
	$op_loadav="load averages";
	$op_mount="mounted on";
	$op_ram="RAM";
	$op_runsin="running since";
	$op_size="size (kB)";
	$op_spec="specifications of";
	$op_start="started";
	$op_used="used (kB)";
		
	if ($language_file ne "") {
		open(LF,"$language_file") || die "Could not open $language_file: $!";
		while (<LF>) {
			# we don't need empty lines and such starting with #
			if (substr($_,0,1) eq "#" || length($_)==1) { # empty lines contain \n, so 1
				next;
			}
			
			chomp;
			$poscolon=(index($_,":"));
			$var=substr($_,0,$poscolon);
			$trans=substr($_,($poscolon+1));
			
			$_=$var;
			SWITCH: {
				if (/^asof/) {$op_asof=$trans; last SWITCH;}
				if (/^bestup/) {$op_bestup=$trans; last SWITCH;}
				if (/^bogomi/) {$op_bogomi=$trans; last SWITCH;}
				if (/^curupt/) {$op_curupt=$trans; last SWITCH;}
				if (/^days/) {$op_days=$trans; last SWITCH;}
				if (/^dev/) {$op_dev=$trans; last SWITCH;}
				if (/^free/) {$op_free=$trans; last SWITCH;}
				if (/^genby/) {$op_genby=$trans; last SWITCH;}
				if (/^hds/) {$op_hds=$trans; last SWITCH;}
				if (/^manu/) {$op_manu=$trans; last SWITCH;}
				if (/^model/) {$op_model=$trans; last SWITCH;}
				if (/^mopo/) {$op_mopo=$trans; last SWITCH;}
				if (/^mount/) {$op_mount=$trans; last SWITCH;}
				if (/^osver/) {$op_osver=$trans; last SWITCH;}
				if (/^peruse/) {$op_peruse=$trans; last SWITCH;}
				if (/^proc/) {$op_proc=$trans; last SWITCH;}
				if (/^lamin/) {$op_lamin=$trans; last SWITCH;}
				if (/^la5min/) {$op_la5min=$trans; last SWITCH;}
				if (/^la15min/) {$op_la15min=$trans; last SWITCH;}
				if (/^loadav/) {$op_loadav=$trans; last SWITCH;}
				if (/^ram/) {$op_ram=$trans; last SWITCH;}
				if (/^runsin/) {$op_runsin=$trans; last SWITCH;}
				if (/^size/) {$op_size=$trans; last SWITCH;}
				if (/^spec/) {$op_spec=$trans; last SWITCH;}
				if (/^start/) {$op_start=$trans; last SWITCH;}
				if (/^used/) {$op_used=$trans; last SWITCH;}
			} #end SWITCH
		} #end while
	} #end if
} #end sub language
