diff options
author | Olivier Mehani <shtrom@ssji.net> | 2017-04-27 03:25:32 +0200 |
---|---|---|
committer | Olivier Mehani <shtrom@ssji.net> | 2017-07-22 13:11:52 +0200 |
commit | 67d75f8be5fb70a6cef2ef3b9b8318e768ba23f1 (patch) | |
tree | 8cb7445fcb503cb732f52fd714f573e9a8ac09b5 /openbsd | |
parent | ef3de94fcffbbc87d9290f9c5f8d2f80f79b4ac1 (diff) |
Import install_rtm-freebsd.sh from OVH
Signed-off-by: Olivier Mehani <shtrom@ssji.net>
Diffstat (limited to 'openbsd')
-rw-r--r-- | openbsd/install_rtm-freebsd.sh | 1854 |
1 files changed, 1854 insertions, 0 deletions
diff --git a/openbsd/install_rtm-freebsd.sh b/openbsd/install_rtm-freebsd.sh new file mode 100644 index 0000000..c6b5a28 --- /dev/null +++ b/openbsd/install_rtm-freebsd.sh @@ -0,0 +1,1854 @@ +#!/usr/local/bin/bash + +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH +export PATH + +VERSION="0.9.4-4" +RELEASE_DATE="2016-02-25" + +LC_ALL=POSIX + +DIR="/usr/local/rtm" +DIR_SCRIPTS_DAILY="${DIR}/scripts/daily" +DIR_SCRIPTS_MIN="${DIR}/scripts/min" +DIR_SCRIPTS_HOUR="${DIR}/scripts/hour" +DNSSERVER="213.186.33.99" +SCRIPTS_TO_INSTALL="check kernel release usage usage_root hwinfo hwinfo_root hddinfo smart raid listen_ports" + +RTM_PL=$DIR/bin/rtm-${VERSION}.pl +RTM_SH=$DIR/bin/rtm +RTM_UPDATE_IP=$DIR/bin/rtm-update-ip.sh + +RTM_REPORT=$DIR/bin/update-report.pl + + +if [ "$SCREENDIR" != "" ]; then SCREEN="$SCREENDIR -d -m"; fi +SCRIPTDIR="$DIR/scripts" + +# add user ovh and group ovh to run rtm on it: +if [ -z "`pw usershow ovh 2>\/dev\/null`" ]; then + pw useradd ovh -d /nonexistent -c "OVH user for RTM running" -s /usr/bin/false -P no +fi + +OVHUID=`pw usershow ovh | cut -d: -f3` +OVHGID=`pw usershow ovh | cut -d: -f4` + + +# +# Generate update-report.pl file +generate_update_report() { + echo "Generating update-report.pl..." + cat << EOF > $RTM_REPORT +#! /usr/local/bin/perl +# version: $VERSION ($RELEASE_DATE) + +\$ENV{"LC_ALL"} = "POSIX"; + +EOF + cat <<'EOF' >> $RTM_REPORT +#!/usr/local/bin/perl + +use strict; +use Socket; + +EOF + echo "my \$destination_ip = '$ip';" >> $RTM_REPORT + cat <<'EOF' >> $RTM_REPORT +my $message = <>; +chomp($message); +exit if ($message eq ''); + +send_info($message); + +sub send_info { + my $message = shift; + $message = "rtm dINFO_RTM_update|$message\n"; + my $port = 6100 + int(rand(100)); + my $ok = eval { + local $SIG{ALRM} = sub { print "rtm timeout\n"; die; }; + alarm(10); + + my $proto = getprotobyname('udp'); + socket(Socket_Handle, PF_INET, SOCK_DGRAM, $proto); + my $iaddr = gethostbyname($destination_ip); + my $sin = sockaddr_in("$port", $iaddr); + send(Socket_Handle, "$message", 10, $sin); + print "$message"; + alarm(0); + }; + if (!defined($ok)) { + warn "error: $@\n"; + } +} +EOF + chown 0:0 "$RTM_REPORT" + chmod 750 "$RTM_REPORT" +} + +# check if script dirs point to reasonable paths +for scr in "$DIR_SCRIPTS_DAILY", "$DIR_SCRIPTS_MIN", "$DIR_SCRIPTS_HOUR"; do + if ! echo "$scr" | grep -q '^/usr/local/'; then + echo "invalid script directory: $scr" >&2 + exit 1 + fi +done +# Remove old scripts dirs so there are no unwanted scripts hanging +# around +rm -rf "$DIR_SCRIPTS_DAILY" +rm -rf "$DIR_SCRIPTS_MIN" +rm -rf "$DIR_SCRIPTS_HOUR" + +if [ ! -e "$DIR" ]; then mkdir -p $DIR; fi +if [ ! -e "$DIR_SCRIPTS_DAILY" ]; then mkdir -p "$DIR_SCRIPTS_DAILY"; fi +if [ ! -e "$DIR_SCRIPTS_MIN" ]; then mkdir -p "$DIR_SCRIPTS_MIN"; fi +if [ ! -e "$DIR_SCRIPTS_HOUR" ]; then mkdir -p "$DIR_SCRIPTS_HOUR"; fi +if [ ! -e "$DIR/bin" ]; then mkdir -p "$DIR/bin"; fi +if [ ! -e "$DIR/etc" ]; then mkdir -p "$DIR/etc"; fi + +get_interface_ip() { + iface=$1 + ifconfig $iface 2>\/dev\/null | grep "inet.*netmask" | awk '{print $2}' | egrep '[0-9]+(\.[0-9]+){3}' +} + +mainif="$(netstat -rn|grep "^default" | awk '{print $NF}'|sort|uniq|head -n1)" +mainip="$(get_interface_ip $mainif)" + +arpa=`echo "$mainip" | sed "s/\./ /g" | awk '{print $3"."$2"."$1}'`; +ip=`host -t A mrtg.$arpa.in-addr.arpa $DNSSERVER 2>/dev/null | grep "has address" | awk '{print $4}' | egrep '[0-9]+(\.[0-9]+){3}'` + +if [ -z "$ip" ]; then + echo "No IP from OVH network !" + exit 1; +fi +echo $ip > "$DIR/etc/rtm-ip" + + +cd `dirname $0` + +upgrade=$1 +if [ "$upgrade" != "-u" ]; then + upgrade="" +fi + +if [ -z "`which bzip2`" ]; then + echo "bzip2 not found!!" + echo "Please install bzip2." + exit +fi + +# +# Generate /scripts/min/check.pl file +generate_check() { + echo "Generating $DIR_SCRIPTS_MIN/check.pl..." + cat << EOF > $DIR_SCRIPTS_MIN/check.pl +#! /usr/local/bin/perl +# version: $VERSION ($RELEASE_DATE) + +\$ENV{"LC_ALL"} = "POSIX"; + +EOF + cat <<'EOF' >> $DIR_SCRIPTS_MIN/check.pl +use strict; + +# nothing here at the moment +EOF + chown 0:0 "$DIR_SCRIPTS_MIN/check.pl" + chmod 750 "$DIR_SCRIPTS_MIN/check.pl" +} + +# +# Generate /scripts/day/kernel.sh file +generate_kernel() { + echo "Generating $DIR_SCRIPTS_DAILY/kernel.sh..." + cat << EOF > $DIR_SCRIPTS_DAILY/kernel.sh +#! /bin/sh +# version: $VERSION ($RELEASE_DATE) + +LC_ALL=POSIX + +EOF + cat <<'EOF' >> $DIR_SCRIPTS_DAILY/kernel.sh +# +# This script determines running kernel version. +# + + +rel=`uname -r` +ver=`uname -v` + +if [ ! -z "$ver" ]; then + echo "dINFO_KERNEL_release|$rel"; + echo "dINFO_KERNEL_version|$ver" +fi +EOF + chown $OVHUID:$OVHGID "$DIR_SCRIPTS_DAILY/kernel.sh" + chmod 750 "$DIR_SCRIPTS_DAILY/kernel.sh" +} + +# +# Generate /scripts/day/release.sh file +generate_release() { + echo "Generating $DIR_SCRIPTS_DAILY/release.sh..." + cat << EOF > $DIR_SCRIPTS_DAILY/release.sh +#! /bin/sh +# version: $VERSION ($RELEASE_DATE) + +LC_ALL=POSIX + +EOF + cat <<'EOF' >> $DIR_SCRIPTS_DAILY/release.sh +# +# The script below is used to determine OS release. +# + +distro=`uname -sr` +test -f /etc/ovhrelease && release_ovh=`cat /etc/ovhrelease` + +echo "dINFO_RELEASE_os|$distro"; +echo "dINFO_RELEASE_ovh|$release_ovh"; +EOF + chown $OVHUID:$OVHGID "$DIR_SCRIPTS_DAILY/release.sh" + chmod 750 "$DIR_SCRIPTS_DAILY/release.sh" +} + +# +# Generate /scripts/hour/raid.pl file +generate_raid() { + echo "Generating $DIR_SCRIPTS_HOUR/raid.pl..." + cat << EOF > $DIR_SCRIPTS_HOUR/raid.pl +#! /usr/local/bin/perl +# version: $VERSION ($RELEASE_DATE) + +\$ENV{"LC_ALL"} = "POSIX"; + +EOF + cat <<'EOF' >> $DIR_SCRIPTS_HOUR/raid.pl +# +# Shows information about RAID arrays such as disks capacity, models, overal array status. +# + + +use strict; +use IO::Select; + +chomp(my @sysctl = `\/sbin\/sysctl dev 2>\/dev\/null`); + +# TODO: Soft RAID with: +# - gvinum and raid-5 +# +# TODO: Mylex RAID support (no tools found. Only megarc/amrstat but not working for all cards) + + +# gmirror/gstripe: +chomp(my @gmirror = `gmirror status -s 2>\/dev\/null`); +chomp(my @gstripe = `gstripe status -s 2>\/dev\/null`); +if($#gmirror != -1 or $#gstripe != -1){ + my %raid; + + # gmirror/gstripe enabled + foreach(@gmirror, @gstripe){ + # mirror/home DEGRADED da0s1f (65%) + next unless $_ =~ m/^\s*(mirror|stripe)\/(\S+)\s+(\S+)\s+(.*?)(?:\s+\((\d+)%\))?\s*$/; + $raid{$1}{$2}{state} = $3; + my %tmp = (name=>$4); + $tmp{syncpercent} = $5 if $5; + push @{$raid{$1}{$2}{disks}}, \%tmp; + } + + foreach my $type (keys %raid){ + foreach my $vol (keys %{$raid{$type}}){ + my @data; + if($type eq 'mirror'){ + chomp(@data = `gmirror list $vol 2>\/dev\/null`); + print "hHW_SCSIRAID_UNIT_$type\_vol-$vol\_type|MIRROR\n"; + }elsif($type eq 'stripe'){ + chomp(@data = `gstripe list $vol 2>\/dev\/null`); + print "hHW_SCSIRAID_UNIT_$type\_vol-$vol\_type|STRIPE\n"; + } + + # volume info: + foreach(@data){ + last if /Consumers/i; + + print "hHW_SCSIRAID_UNIT_$type\_vol-$vol\_capacity|$1 $2\n" + if /Mediasize:\s+\d+\s+\((\d+)(\w+)\)$/i; + + print "hHW_SCSIRAID_UNIT_$type\_vol-$vol\_phys|$1\n" + if /Components:\s+(\d+)$/i; + + if(/State:\s+(\w+)$/){ + my $st = $1; + $st eq 'COMPLETE' and $st = 'OK'; + print "hHW_SCSIRAID_UNIT_$type\_vol-$vol\_status|$st\n" + } + + print "hHW_SCSIRAID_UNIT_$type\_vol-$vol\_flags|$1\n" + if /Flags:\s*(\w+)$/; + } + + # disk info: + my ($ldn, $ldi); # lastDiskName and lastDiskId + my $consumers = 0; + + foreach(@data){ + # skip volume info: + $consumers = 1 if /Consumers/i; + next unless $consumers; + + + ($ldi, $ldn) = ($1-1, $2) + if /^(\d+)\.\s*Name:\s*(\S+)$/; + + print "hHW_SCSIRAID_PORT_$type\_vol-$vol\_phy$ldi\_capacity|$1 $2\n" + if /Mediasize:\s+\d+\s+\((\d+)(\w+)\)$/i; + + print "hHW_SCSIRAID_PORT_$type\_vol-$vol\_phy$ldi\_status|$1\n" + if /State:\s*(\w+)$/; + + print "hHW_SCSIRAID_PORT_$type\_vol-$vol\_phy$ldi\_flags|$1\n" + if /Flags:\s*(\S+)$/; + + print "hHW_SCSIRAID_PORT_$type\_vol-$vol\_phy$ldi\_syncprogress|$1\n" + if /Synchronized:\s*(\d+)%/; + } + + } + } +} + + +# 3ware (all) +if (map {$_ =~ /3ware/i} @sysctl) { + my (%units, @controlers, %models); + + chomp(my @twCliInfo = `\/usr\/local\/sbin\/tw_cli info 2>\/dev\/null`); + + # parse tw_cli basis view: + foreach my $line (@twCliInfo) { + if ($line =~ m/Controller (\d+):/) { push @controlers, $1;} + if ($line =~ /^c(\d+)\s+(\S+)\s+/) { push @controlers, $1; $models{$1} = $2;} + } + + + foreach my $controler (@controlers) { + chomp(@twCliInfo = `\/usr\/local\/sbin\/tw_cli info c$controler 2>\/dev\/null`); + + # parse tw_cli detailed info: + foreach my $line (@twCliInfo) { + if ( $line =~ m/Unit\s(\d):\s+(RAID\s+\d+|[^\s]+)\s([^\s]+)\s([^\s]+)[^:]+:\s(.+)/) { + print "hHW_SCSIRAID_UNIT_c$controler\_u$1_capacity|$3 $4\n"; + print "hHW_SCSIRAID_UNIT_c$controler\_u$1_type|$2\n"; + print "hHW_SCSIRAID_UNIT_c$controler\_u$1_status|$5\n"; + } + if ( $line =~ m/Port\s(\d+):\s([^\s]+)\s([^\s]+)\s([^\s]+)\s([^\s]+)\s([^\s]+)[^:]+:\s([^\(]+)\(unit\s(\d+)/) { + print "hHW_SCSIRAID_PORT_c$controler\_u$8_phy$1_capacity|$5 $6\n"; + print "hHW_SCSIRAID_PORT_c$controler\_u$8_phy$1_model|$2 $3\n"; + print "hHW_SCSIRAID_PORT_c$controler\_u$8_phy$1_status|$7\n"; + if (! exists $units{$controler}{$8}) {$units{$controler}{$8} = 0;} + $units{$controler}{$8} = $units{$controler}{$8} + 1; + } + + # 3ware models: 7xxx, 8xxx, 9xxx, 95xx: + # Units: + # ONLY FreeBSD: + # Unit UnitType Status %RCmpl %V/I/M Stripe Size(GB) Cache AVrfy + if ( $line =~ /^u(\d+)\s+(RAID\-\d+)\s+(\S+)\s+\S+\s+\S+\s+(\S+)\s+(\S+).*/ ) + { + print "hHW_SCSIRAID_UNIT_c$controler\_u$1_capacity|$5 GB\n"; + print "hHW_SCSIRAID_UNIT_c$controler\_u$1_type|$2\n"; + print "hHW_SCSIRAID_UNIT_c$controler\_u$1_status|$3\n"; + } + # Ports: + if ( $line =~ /^p(\d+)\s+(\S+)\s+u(\S+)\s+(\S+\s\S+)\s+(\d+)\s+(\S+)\s*$/ ) + { + + print "hHW_SCSIRAID_PORT_c$controler\_u$3_phy$1_capacity|$4\n"; + print "hHW_SCSIRAID_PORT_c$controler\_u$3_phy$1_serial|$6\n"; + print "hHW_SCSIRAID_PORT_c$controler\_u$3_phy$1_status|$2\n"; + + if (! exists $units{$controler}{$3}) {$units{$controler}{$3} = 0;} + $units{$controler}{$3} += 1 if $2 ne "NOT-PRESENT"; + + my $p = $1; + my $u = $3; + chomp(my $model = `\/usr\/local\/sbin\/tw_cli info c$controler p$p model 2>\/dev\/null`); + print "hHW_SCSIRAID_PORT_c$controler\_u$u\_phy$p\_model|$1\n" + if $model =~ /Model\s+=\s+(.*)$/; + + } + } + foreach (keys %{$units{$controler}}) {print "hHW_SCSIRAID_UNIT_c$controler\_u$_\_phys|".($units{$controler}{$_})."\n";} + print "hHW_SCSIRAID_CONTROLLER_c$controler\_model|$models{$controler}\n" if defined $models{$controler}; + } +} elsif (map {$_ =~ /LSILogic/i} @sysctl) { + # LSILOGIC MPT adapter: + + my (%units, $count); + chomp(my @dmesg = `dmesg | grep "mpt"`); + chomp(my @camcontrolDevlist = `\/sbin\/camcontrol devlist`); + + # check UNIT number: + $count = 0; + while(my $a = grep {$_ =~ /^mpt$count: (\d+) Active Volume/i} @dmesg) { + $units{$count}{volumeCount} = $a; + $count++; + } + + # foreach unit (mpt0, mpt1, ...) + foreach my $u (keys %units){ + + foreach(@dmesg){ + /^mpt$u:\s*(\d+).*Drive Members/i and $units{$u}{driveCount} = $1; + /^mpt$u: Capabilities: \( (.*?) \)$/i and $units{$u}{capabilities} = $1; + } + + # foreach volume: (vol0, vol1, ...) + for(my $volume=0; $volume<$units{$u}{volumeCount}; $volume++){ + my @revDmesg = reverse @dmesg; + my ($lastVolStatus, $lastSyncPercent, $lastFlags); + + foreach(@revDmesg) { + # print capacity of the volume using bus/target/lun from dmesg, + # from the line with unix device (ad0) at bus .. target .. lun .. + # WARNING: we assume that unix device numeration is same as scsi volume numeration + # example: + # daX is on scsi volume X + # TODO improve this + if(/.*$volume at mpt$u bus (\d+) target (\d+) lun (\d+)/){ + chomp(my $c = `\/sbin\/camcontrol readcap $1:$2:$3 -q`); + if($c =~ /^(\d+),\s*(\d+)$/){ + print "hHW_SCSIRAID_UNIT_mpt$u\_vol-id$volume\_capacity|".(normalize($1*$2))."\n"; + } + } + + + next unless /mpt$u:vol$volume/; + + # status: + if(/: Status \( (.*?) \)$/ and not $lastFlags){ $lastFlags = uc $1; } + + # state: + if(/: RAID-\d+ - (.*)$/i){ $lastVolStatus = uc $1;} + + # sync percentage: + if(/: (\d+) of (\d+) blocks remaining/i){ $lastSyncPercent = int($1/$2 * 100); } + + # we search backwards only till this line: + last if /Volume Status Changed/; + } + + print "hHW_SCSIRAID_UNIT_mpt$u\_vol-id$volume\_flags|$lastFlags\n"; + print "hHW_SCSIRAID_UNIT_mpt$u\_vol-id$volume\_status|$lastVolStatus\n"; + print "hHW_SCSIRAID_UNIT_mpt$u\_vol-id$volume\_type|$units{$u}{capabilities}\n"; + print "hHW_SCSIRAID_UNIT_mpt$u\_vol-id$volume\_phys|".$units{$u}{driveCount}."\n"; + print "hHW_SCSIRAID_UNIT_mpt$u\_vol-id$volume\_syncprogress|$lastSyncPercent\n" + if $lastFlags =~ /syncing/i; + + # foreach drive in volume + for(my $d=0; $d<$units{$u}{driveCount}; $d++){ + my $lastStatus = ''; + my $lastFlags = ''; + + foreach(@revDmesg) { + next unless /^\(mpt$u:vol$volume:$d\):/; + + if(/\(mpt$u:vol$volume:$d\): Status \( (.*) \)$/ and not $lastFlags){ + $lastFlags = uc $1; + } elsif(/\(mpt$u:vol$volume:$d\): Physical.* Pass-thru \(mpt$u:(\d+):(\d+):(\d+)\)\s*$/){ + my ($bus, $target, $lun) = ($1, $2, $3); + foreach(@camcontrolDevlist){ + /<(.*?)>\s+at scbus$bus target $target lun $lun/ + and print "hHW_SCSIRAID_PORT_mpt$u\_vol-id$volume\_phy$d\_model|$1\n" + and last; + }; + chomp(my $c = `\/sbin\/camcontrol readcap $bus:$target:$lun -q`); + print "hHW_SCSIRAID_PORT_mpt$u\_vol-id$volume\_phy$d\_capacity|".(normalize($1*$2))."\n" + if ($c =~ /^(\d+),\s*(\d+)$/); + # none + } elsif(/\(mpt$u:vol$volume:$d\): Volume Status Changed.*$/){ + # none + } elsif(/\(mpt$u:vol$volume:$d\): (.*)$/ and not $lastStatus){ + $lastStatus = uc $1; + } + + # we search backwards only till this line: + last if /Physical Disk Status Changed/; + } + $lastFlags = 'NONE' if $lastFlags eq 'ENABLED' or $lastFlags eq ''; + + print "hHW_SCSIRAID_PORT_mpt$u\_vol-id$volume\_phy$d\_status|$lastStatus\n"; + print "hHW_SCSIRAID_PORT_mpt$u\_vol-id$volume\_phy$d\_flags|$lastFlags\n"; + } + } + } + +} + +sub normalize { + my $bytes = shift || return 0; + my @units = qw/KB MB GB TB PB/; + my $i = -1; + + # if we get bytes/MB/TB and still want to normalize: + if($bytes =~ /^(\d+)\s*([a-zA-Z]+)\s*$/){ + $bytes = $1; + my $unit = $2; + foreach(@units){ + $i++; + last if uc($unit) eq $_; + } + } + return -1 if $bytes > 1024 and $i >= $#units; # error + + while($bytes > 1024){ + $i++; + $bytes = int($bytes/1024); + } + return $bytes." $units[$i]"; +} + +EOF + chown 0:0 "$DIR_SCRIPTS_HOUR/raid.pl" + chmod 750 "$DIR_SCRIPTS_HOUR/raid.pl" +} + +# +# Generate /scripts/hour/smart.pl file +generate_smart() { + echo "Generating $DIR_SCRIPTS_HOUR/smart.pl..." + cat << EOF > $DIR_SCRIPTS_HOUR/smart.pl +#! /usr/local/bin/perl +# version: $VERSION ($RELEASE_DATE) + +\$ENV{"LC_ALL"} = "POSIX"; + +EOF + cat <<'EOF' >> $DIR_SCRIPTS_HOUR/smart.pl +# +# This script monitors SMART Errors count and critical SMART values. +# + +# TODO: LSI Logic or Mylex smart error detection (via scsi?) + +use strict; +use IO::Select; + +exit if (! -e '/usr/local/sbin/smartctl'); + +my %smartData; + +sub parse_smartctl_line { + my $line = shift; + my $dev = shift; + + if($line =~ /^SMART Attributes Data Structure revision number: (\d+\w*)$/) { + print "hINFO_RTM_SMART_versionnotcompatible|$1\n" + unless $1 eq '10'; + } + + if($line =~ /^SMART overall-health.*result: (\w+)$/) { + # $1 - PASSED or problem description + print "hINFO_HDD_$dev\_SMART-SELF_overall-health-test|$1\n" + if $1 and $1 ne 'PASSED'; + } + + # Check for Vendor Specific SMART Attributes with Thresholds: + if ($line =~ /^(\s{0,2}\d{1,3})\s+(\w+)\s+0x[\dabcdef]{4}\s+(\d+)\s+(\d+)\s+(\d+)\s+(\w+)\s+\w+\s+(\w+)\s+\d+.*$/) { + # $1 - ID arr[0] + # $2 - name arr[1] + # $3 - value arr[2] + # $4 - worst arr[3] + # $5 - thresh arr[4] + # $6 - type arr[5] + # $7 - when failed arr[6] + my @arr = ($1, $2, $3, $4, $5, $6, $7); + + # we want only Pre-fail type attributes: + if($6 =~ /Pre-fail/i) { + my $n = $arr[1]; + $n =~ s/_/-/g; + $n = lc($n); + + print "hINFO_HDD_$dev\_SMART-ATTR_$n|$arr[6]\n" + if $arr[6] ne "-"; + } + } + + # check for number of ATA errors: + if ($line =~ /^ATA Error Count: (\d+)$/) { + print "hINFO_HDD_$dev\_SMART-ATA_error-count|$1\n" + if $1 > 0; + } +} + +sub check_ide { + chomp(my $d = `\/sbin\/sysctl -n kern.disks 2>\/dev\/null`); + my @diskList = split ' ', $d; + + foreach my $dev (@diskList) { + next if $dev =~ /^ad/; + chomp(my @smartctlData = `\/usr\/local\/sbin\/smartctl -a \/dev\/$dev 2>\/dev\/null`); + foreach my $line (@smartctlData) { + parse_smartctl_line($line, $dev); + } + } +} + +sub check_scsi { + chomp(my $d = `\/sbin\/sysctl -n kern.disks 2>\/dev\/null`); + my @diskList = split ' ', $d; + + foreach my $dev (@diskList) { + next if $dev =~ /^da/; + chomp(my @smartctlData = `\/usr\/local\/sbin\/smartctl -a \/dev\/$dev 2>\/dev\/null`); + foreach my $line (@smartctlData) { + if ($line =~ /^read:.+(\d+)$/) { + print "hINFO_HDD_$dev\_SMART_uncorrected-read-errors|$1\n"; + } + if ($line =~ /^write:.+(\d+)$/) { + print "hINFO_HDD_$dev\_SMART_uncorrected-write-errors|$1\n"; + } + } + } +} + +sub _3ware_get_ports_for_disk { + my $disk = shift; + my @ports = (); + open my $TWCLI_OUTPUT, "\/usr\/local\/sbin\/tw_cli info c$disk 2>\/dev\/null |" or die("failed to run 'tw_cli'"); + while (<$TWCLI_OUTPUT>) { + next unless /^p(\d+) /; + push @ports, $1; + } + close $TWCLI_OUTPUT; + return @ports; +} + +sub check_3ware { + my (@controlers, $read_set); + chomp(my @twCliInfo = `\/usr\/local\/sbin\/tw_cli info 2>\/dev\/null`); + map {push @controlers, $1 if /^c(\d+)\s+/} @twCliInfo; + my $name; + $name = "twe" if </dev/twe?>; # twe for 6/7/8000 series controller + $name = "twa" if </dev/twa?>; # twa for 9000 series controller + return unless $name; + + $read_set = IO::Select->new(); + foreach my $controler (@controlers) { + next unless $controler =~ /^\d+$/; + foreach my $port (_3ware_get_ports_for_disk($controler)) { + pipe my $P_READ, my $P_WRITE or die "pipe(): $!"; + my $pid = fork(); + die "cannot fork: $!" if $pid < 0; + if ($pid == 0) { + open my $SMARTCTL_OUTPUT, "\/usr\/local\/sbin\/smartctl --device=3ware,$port /dev/${name}$controler -a |" or die("failed to run smartctl"); + close $P_READ; + select($P_WRITE); + while (<$SMARTCTL_OUTPUT>) { + parse_smartctl_line($_, "${name}$controler-$port"); + } + exit(); + } + close $P_WRITE; + $read_set->add($P_READ); + } + } + while (my @fds = $read_set->can_read()) { + foreach my $fd (@fds) { + my @lines = <$fd>; + if (!@lines) { + close $fd; + next; + } + print join('', @lines); + } + } + while (waitpid(-1, 0) > 0) { + } +} + + +check_ide(); +check_scsi(); +check_3ware(); +EOF + chown 0:0 "$DIR_SCRIPTS_HOUR/smart.pl" + chmod 750 "$DIR_SCRIPTS_HOUR/smart.pl" +} + +# +# Generate /scripts/hour/listen_ports.pl file +generate_listen_ports() { + echo "Generating $DIR_SCRIPTS_HOUR/listen_ports.pl..." + cat << EOF > $DIR_SCRIPTS_HOUR/listen_ports.pl +#! /usr/local/bin/perl +# version: $VERSION ($RELEASE_DATE) + +\$ENV{"LC_ALL"} = "POSIX"; + +EOF + cat <<'EOF' >> $DIR_SCRIPTS_HOUR/listen_ports.pl +# +# This script monitors all listening TCP connections in OS. +# + +use strict; +use utf8; # for \x{nnn} regex + +my (@netstatTable, $line, $socketInfo, $procInfo, @tempTable, $port, $pid, $procName, $ip, $cmdline, $exe, @status, $statusLine, $uid, @passwd, $passwdLine, %passwdHash); + +chomp(@netstatTable = `\/usr\/bin\/sockstat -lP tcp \| \/usr\/bin\/awk \'NR>1 {print \$1"|"\$2"|"\$3"|"\$6;}\'`); +map {s/\|\*:/\|0.0.0.0:/; s/:(\d+)$/\|$1/;} @netstatTable; # change to behave as on linux systems +# $netstatTable[x] = 'bind|named|517|127.0.0.1|953' + +# username <=> uid transformation table +open(FILE, "/etc/passwd"); +chomp(my @passwd = <FILE>); +close(FILE); +my %passwdHash; +foreach (@passwd) { + /^([^:]+):[^:+]:(\d+):/; + $passwdHash{$1} = $2; + $passwdHash{$2} = $1; +} + + +foreach (@netstatTable) { + my @lineTab = split /\|/; + + if ($lineTab[0] =~ /^\d+$/) { + push @lineTab, $lineTab[0]; + $lineTab[0] = (defined $passwdHash{$lineTab[0]})?$passwdHash{$lineTab[0]}:'-'; + } elsif (defined $passwdHash{$lineTab[0]}) { + push @lineTab, $passwdHash{$lineTab[0]}; + } else { + push @lineTab, '-'; + } + + # get cmdline from procfs: + open(FILE, "/proc/$lineTab[2]/cmdline"); + chomp(my $cmdline = <FILE>); + $cmdline =~ s/\x{0}/ /g; + close(FILE); + push @lineTab, $cmdline; + + # exe: + push @lineTab, readlink("/proc/$lineTab[2]/file"); + +# $lineTab = [ +# 'bind', username +# 'named', procname +# '517', PID +# '::1', listen IP +# '953', listen PORT +# '53', UID +# '/usr/sbin/named -t /var/named -u bind ', cmdline +# '/usr/sbin/named' exe +# ]; + + print "hINFO_TCP_LISTEN_IP-$lineTab[3]\_PORT-$lineTab[4]\_pid\|$lineTab[2]\n"; + print "hINFO_TCP_LISTEN_IP-$lineTab[3]\_PORT-$lineTab[4]\_procname\|$lineTab[1]\n"; + print "hINFO_TCP_LISTEN_IP-$lineTab[3]\_PORT-$lineTab[4]\_cmdline\|$lineTab[6]\n"; + print "hINFO_TCP_LISTEN_IP-$lineTab[3]\_PORT-$lineTab[4]\_exe\|$lineTab[7]\n"; + print "hINFO_TCP_LISTEN_IP-$lineTab[3]\_PORT-$lineTab[4]\_username\|$lineTab[0]\n"; + print "hINFO_TCP_LISTEN_IP-$lineTab[3]\_PORT-$lineTab[4]\_uid\|$lineTab[5]\n"; + +} + + +EOF + chown 0:0 "$DIR_SCRIPTS_HOUR/listen_ports.pl" + chmod 750 "$DIR_SCRIPTS_HOUR/listen_ports.pl" +} + +# +# Generate /scripts/min/usage.pl file +generate_usage() { + echo "Generating $DIR_SCRIPTS_MIN/usage.pl..." + cat << EOF > $DIR_SCRIPTS_MIN/usage.pl +#! /usr/local/bin/perl +# version: $VERSION ($RELEASE_DATE) + +\$ENV{"LC_ALL"} = "POSIX"; + +EOF + cat <<'EOF' >> $DIR_SCRIPTS_MIN/usage.pl +# +# Script used for monitoring overall server load. Memory/swap/load/cpu_usage are entrained. +# + + +use strict; + +# tmp file for storing cpu stats from /proc/stat (Linux) or sysctl kern.cp_time (FreeBSD) +my $CPU_STATS_FILE = "/tmp/cpu_stats"; + +sub send_loadavg { + my %loadavg = get_loadavg(); + print "mINFO_LOAD_loadavg1|" . $loadavg{'loadavg1'} . "\n"; + print "mINFO_LOAD_loadavg2|" . $loadavg{'loadavg2'} . "\n"; + print "mINFO_LOAD_loadavg3|" . $loadavg{'loadavg3'} . "\n"; +} + +sub send_mem_swap_usage { + my %mem_swap_usage = get_mem_swap_usage(); + print "mINFO_MEM_memusage|" . $mem_swap_usage{'mem_used_pr'} . "\n"; + print "mINFO_MEM_swapusage|" . $mem_swap_usage{'swap_used_pr'} . "\n"; +} + +sub send_cpu_usage { + my $cpu_usage = get_cpu_usage(); + print "mINFO_CPU_usage|" . $cpu_usage . "\n"; +} + +sub send_hdd_usage { + my %hdd_usage = get_hdd_usage(); + foreach (keys %{$hdd_usage{'usage'}}) { + print "mINFO_PART_$_\_mount|" . $hdd_usage{'mount'}{$_} . "\n"; + print "mINFO_PART_$_\_usage|" . $hdd_usage{'usage'}{$_} . "\n"; + print "mINFO_PART_$_\_inodes|" . $hdd_usage{'inodes'}{$_} . "\n"; + } +} + +sub get_loadavg { + chomp(my $load = `\/sbin\/sysctl vm.loadavg 2>\/dev\/null`); + $load =~ s/.*{\s*(.*)\s*}.*/$1/; + chomp(my @load = split(/\s/, $load)); + return ('loadavg1' => $load[0], + 'loadavg2' => $load[1], + 'loadavg3' => $load[2],); +} + + +sub get_cpu_usage { + my ($cpu_usage, @cpu_usage1, @cpu_usage2, $delta); + @cpu_usage1 = (0, 0, 0, 0); + @cpu_usage2 = (0, 0, 0, 0); + + chomp(my $cpu_time = `\/sbin\/sysctl kern.cp_time 2>\/dev\/null`); + if ($cpu_time =~ /^kern.cp_time:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*$/i) { + # CPU USAGE LINUX: user, nice, system, idle + # CPU USAGE FreeBSD: user, nice, system, interrupt, idle + @cpu_usage2 = ($1, $2, $3, $5); + } + + + # get historical stats: + if(-e $CPU_STATS_FILE) { + open(TMP, "+< $CPU_STATS_FILE") or die "$CPU_STATS_FILE: $!\n"; + while (<TMP>) { + if (/^kern.cp_time:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*$/i) { + @cpu_usage1 = ($1, $2, $3, $5); + } + } + seek(TMP, 0, 0); + } else { + # if there is no historical data, use actual as a historical: + open(TMP, "> $CPU_STATS_FILE") or die "$CPU_STATS_FILE: $!\n"; + @cpu_usage1 = @cpu_usage2 ; + } + print TMP $cpu_time; + close (TMP); + + $delta = $cpu_usage2[0]+$cpu_usage2[1]+$cpu_usage2[2]+$cpu_usage2[3]- + ($cpu_usage1[0]+$cpu_usage1[1]+$cpu_usage1[2]+$cpu_usage1[3]); + if ($delta > 0) { + $cpu_usage = sprintf("%d", 100-(($cpu_usage2[3]-$cpu_usage1[3])/$delta*100)); + } else { + $cpu_usage = 0; + } + return $cpu_usage; +} + +sub get_mem_swap_usage { + + my %mem_swap_usage = (); + chomp(my @swap = `\/sbin\/swapctl -kl \| grep "\/dev"`); + my %swap; + map { /^\/dev.*\s+(\d+)\s+(\d+)\s*$/; $swap{'total'}+=($1*1024); $swap{'used'}+=($2*1024);} @swap; # when we have more than one swap partition + + $mem_swap_usage{'swap_total'} = $swap{'total'}; + $mem_swap_usage{'swap_used'} = $swap{'used'}; + + if ($swap{'total'} == 0) { + # prevent division by zero + $mem_swap_usage{'swap_used_pr'} = 0; + } else { + $mem_swap_usage{'swap_used_pr'} = sprintf("%d", $swap{'used'}/$swap{'total'}*100); + } + + my %mem_usage; + + # page size + chomp(my $page_size = `\/sbin\/sysctl -n vm.stats.vm.v_page_size 2>\/dev\/null`); + + # number of total memory pages + chomp(my $mem_size_c = `\/sbin\/sysctl -n vm.stats.vm.v_page_count 2>\/dev\/null`); + + # pages without data content + chomp(my $free_c = `\/sbin\/sysctl -n vm.stats.vm.v_free_count`); + + # pages that have percolated from inactive to a status where they maintain their data, but can often be immediately reused + chomp(my $cached_c = `\/sbin\/sysctl -n vm.stats.vm.v_cache_count 2>\/dev\/null`); + + # pages used for filesystem buffers + chomp(my $buffers = `\/sbin\/sysctl -n vfs.bufspace 2>\/dev\/null`); + + # pages that are fixed into memory, usually for kernel purposes, but also sometimes for special use in processes + chomp(my $wired_c = `\/sbin\/sysctl -n vm.stats.vm.v_wire_count 2>\/dev\/null`); + + $mem_usage{'mem_total'} = $page_size * $mem_size_c; + $mem_usage{'mem_free'} = $page_size * $free_c; +# $mem_usage{'mem_shared'} = + $mem_usage{'mem_buffers'} = $buffers; + $mem_usage{'mem_wired'} = $page_size * $wired_c; + $mem_usage{'mem_cached'} = $page_size * $cached_c; + + $mem_usage{'mem_used'} = $mem_usage{'mem_total'} - $mem_usage{'mem_free'} - $mem_usage{'mem_cached'} - $mem_usage{'mem_buffers'}; + $mem_usage{'mem_used_pr'} = ($mem_usage{'mem_total'} > 0)?sprintf("%d", (($mem_usage{'mem_used'})/$mem_usage{'mem_total'}*100)):0; + + return (%mem_swap_usage, %mem_usage); +} + + +sub get_hdd_usage { + my %hdd_usage = (); + + # inodes and usage: + chomp(my @df = `\/bin\/df -lkPi 2>\/dev\/null`); + foreach (@df) { + if (/^(\/dev\/\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)%\s+(\S+)\s+(\S+)\s+(\S+)%\s+(\S+)\s*$/i) { + my $hdd_name = $1; + my $mount = $9; + my $usage = $5; + my $inodes = $8; + + $hdd_name =~ s!^/dev/!!; + $hdd_name =~ s!/!-!g; + $hdd_usage{'usage'}{$hdd_name} = $usage; + $hdd_usage{'mount'}{$hdd_name} = $mount; + $hdd_usage{'inodes'}{$hdd_name} = ($inodes =~ /^\d+$/)?$inodes:0; + } + } + return %hdd_usage; +} + +send_hdd_usage(); +send_mem_swap_usage(); +send_loadavg(); +send_cpu_usage(); +EOF + chown $OVHUID:$OVHGID "$DIR_SCRIPTS_MIN/usage.pl" + chmod 750 "$DIR_SCRIPTS_MIN/usage.pl" +} + +# +# Generate /scripts/min/usage-root.pl file +generate_usage_root() { + echo "Generating $DIR_SCRIPTS_MIN/usage-root.pl..." + cat << EOF > $DIR_SCRIPTS_MIN/usage-root.pl +#! /usr/local/bin/perl +# version: $VERSION ($RELEASE_DATE) + +\$ENV{"LC_ALL"} = "POSIX"; + +EOF + cat <<'EOF' >> $DIR_SCRIPTS_MIN/usage-root.pl +# +# This script monitors TOP processes +# + +use strict; + +sub send_process { + my %processes = get_processes(); + print "mINFO_LOAD_processesactive|" . $processes{'processesactive'} . "\n"; + print "mINFO_LOAD_processesup|" . $processes{'processesup'} . "\n"; +} + +sub send_top_rss { + my $top = get_top_mem_procs(); + my $n = 1; + foreach my $info (@$top) { + my $vsz = $info->[0]; + my $cmd = $info->[1]; + printf "mINFO_MEM_top_mem_%02d_name|%s\n", $n, $cmd; + printf "mINFO_MEM_top_mem_%02d_size|%s\n", $n, $vsz; + ++$n; + } +} + +sub get_processes { + #chomp(my @rtm_sids = `ps --no-headers -C rtm -o sess | sort -n | uniq`); + chomp(my @rtm_sids = `\/bin\/ps -o sid,command \| \/usr\/bin\/grep "rtm" \| \/usr\/bin\/sort -n \| \/usr\/bin\/uniq \| \/usr\/bin\/grep -v grep`); + #my @ps_output = `ps --no-headers -A -o sess,state,command`; + chomp(my @ps_output = `\/bin\/ps -A -o sid,state,command`); + shift @ps_output; # shift headers :) + + my $active = 0; + my $total = 0; + foreach my $line (@ps_output) { + next if $line !~ /(\d+)\s+(\S+)/; + my $sid = $1; + my $state = $2; + if (grep $sid == $_, @rtm_sids) { + next; + } + ++$total; + ++$active if $state =~ /^R/; + } + return ('processesactive' => $active, 'processesup' => $total); +} + +sub get_top_mem_procs { + my @top; + chomp(my @output = `\/bin\/ps -A -ovsz,command \| \/usr\/bin\/awk 'NR>1 {print}' \| \/usr\/bin\/sort -nrk1 \| \/usr\/bin\/grep -v "sort" \| \/usr\/bin\/head -n5`); + return [] unless $? == 0; + foreach (@output) { + next unless m/\s*(\d+)\s+(.+)/; + push @top, [$1, $2]; + } + return \@top; +} + +send_process(); +send_top_rss(); +EOF + chown 0:0 "$DIR_SCRIPTS_MIN/usage-root.pl" + chmod 750 "$DIR_SCRIPTS_MIN/usage-root.pl" +} + +# +# Generate /scripts/hour/hwinfo.pl file +generate_hwinfo() { + echo "Generating $DIR_SCRIPTS_HOUR/hwinfo.pl..." + cat << EOF > $DIR_SCRIPTS_HOUR/hwinfo.pl +#! /usr/local/bin/perl +# version: $VERSION ($RELEASE_DATE) + +\$ENV{"LC_ALL"} = "POSIX"; + +EOF + cat <<'EOF' >> $DIR_SCRIPTS_HOUR/hwinfo.pl +use strict; + +sub send_cpu_info { + my %cpu_info = get_cpu_info(); + print "dHW_CPU_name|" . $cpu_info{'cpu_name'} . "\n"; + print "dHW_CPU_mhz|" . $cpu_info{'cpu_mhz'} . "\n"; + print "dHW_CPU_cache|" . $cpu_info{'cpu_cache'} . "\n"; + print "dHW_CPU_number|" . $cpu_info{'cpu_no'} . "\n"; +} + +sub send_lspci_info { + my %lspci_info = get_lspci_info(); + foreach (keys %lspci_info) { + my $tempKey = $_; + $tempKey =~ s/\:|\.|\_/-/g; + print "dHW_LSPCI_PCI-$tempKey|" . $lspci_info{$_} . "\n"; + } +} + + +sub get_cpu_info { + my %cpu_info = ( 'cpu_no' => 0 ); + + chomp(my @cpuinfo = `\/sbin\/sysctl hw dev.cpu 2>\/dev\/null`); + + foreach(@cpuinfo) { + if (/^hw\.model:\s*(.*)$/) { + $cpu_info{'cpu_name'} = $1; + } + if (/^dev\.cpu\.\d{1,2}\.freq:\s*(.*)$/) { + $cpu_info{'cpu_mhz'} = $1; + } +# TODO - FreeBSD no CPU cache found! +# if ($_ =~ /^cache size/) { +# s/cache size\s+:\s*//g; +# $cpu_info{'cpu_cache'} = $_; +# } + if (/^hw\.ncpu:\s*(.*)$/){ + $cpu_info{'cpu_no'} = $1; + } + + } + + $cpu_info{'cpu_cache'} = ''; + + return %cpu_info; +} + + +sub get_lspci_info { + my %lspci_info = (); + chomp(my @pciconf = `\/usr\/sbin\/pciconf -l 2>\/dev\/null`); + if ($? == 0) { + foreach (@pciconf) { + if(/^\S+@\S+:(\S+):\s+class=0x(\S+)\s+card=0x(\S{4})(\S{4})\s+chip=0x(\S{4})(\S{4})\s+rev=0x(\S+)\s+hdr=0x(\S+)\s*$/){ + # $1 - selector + # $2 - class + # $3 - subvendor device id + # $4 - subvendor id + # $5 - vendor device id + # $6 - vendor id + # $7 - revision + # $8 - describes the header type (see man for pciconf) + my $value = "$6:$5"; + my $selector; + $_ = $1; + while (/(\w+)(\W+)?/g) {$selector .= length($1)==1?"0$1$2":"$1$2";} + $lspci_info{$selector} = $value; + + } + } + } + return %lspci_info; +} + +send_cpu_info(); +send_lspci_info(); +EOF + chown $OVHUID:$OVHGID "$DIR_SCRIPTS_HOUR/hwinfo.pl" + chmod 750 "$DIR_SCRIPTS_HOUR/hwinfo.pl" +} + +# +# Generate /scripts/hour/hwinfo-root.pl file +generate_hwinfo_root() { + echo "Generating $DIR_SCRIPTS_HOUR/hwinfo-root.pl..." + cat << EOF > $DIR_SCRIPTS_HOUR/hwinfo-root.pl +#! /usr/local/bin/perl +# version: $VERSION ($RELEASE_DATE) + +\$ENV{"LC_ALL"} = "POSIX"; + +EOF + cat <<'EOF' >> $DIR_SCRIPTS_HOUR/hwinfo-root.pl +use strict; + +chomp(my @dmesg = `\/sbin\/dmesg 2>\/dev\/null`); +chomp(my @sysctl = `\/sbin\/sysctl dev 2>\/dev\/null`); + +sub send_mainboard_memory_info { + my %mainboard_memory_info = get_mainboard_memory_info(); + print "dHW_MB_manufacture|" . $mainboard_memory_info{'mainboard'}{'manufacture'} . "\n"; + print "dHW_MB_name|" . $mainboard_memory_info{'mainboard'}{'name'} . "\n"; + foreach (keys %{$mainboard_memory_info{'memory'}}) { + print "dHW_MEM_BANK-$_|" . $mainboard_memory_info{'memory'}{$_} . "\n"; + } +} + +sub send_hdd_info { + my $hdd_info = get_hdd_info(); + foreach (keys %{$hdd_info->{'model'}}) { + print "dHW_HDD_$_\_capacity|" . $hdd_info->{'capacity'}{$_} . "\n"; + print "dHW_HDD_$_\_model|" . $hdd_info->{'model'}{$_} . "\n"; + } +} + +sub get_mainboard_memory_info { + my %mainboard_memory_info = (); + my @dmidecode = `dmidecode 2>/dev/null`; + if ($? == 0) { + my $module = ""; + for (my $i = 0; $i < @dmidecode; $i++) { + if($dmidecode[$i] =~ /^\s*Base Board Information/i) { + $dmidecode[$i+1] =~ s/Manufacturer://g; + $dmidecode[$i+2] =~ s/Product Name://g; + $mainboard_memory_info{'mainboard'}{'manufacture'} = $dmidecode[$i+1]; + $mainboard_memory_info{'mainboard'}{'name'} = $dmidecode[$i+2]; + } + if($dmidecode[$i] =~ /^\s*Memory Module Information/i) { + $dmidecode[$i+1] =~ /^\s+(\S+)\s+(\S+)\s+(.+)$/i; + $module = $3; + $module =~ s/\W/-/g; + chomp($module); + } + if(($dmidecode[$i] =~ /^\s+Installed Size:/i) && ($module =~ /\S+/)) { + $module =~ s/#/_/; + $dmidecode[$i] =~ s/Installed Size://g; + $mainboard_memory_info{'memory'}{$module} = $dmidecode[$i]; + $mainboard_memory_info{'memory'}{$module} =~ s/^\s+//; + chomp($mainboard_memory_info{'memory'}{$module}); + $module = ""; + } + } + if (!defined $mainboard_memory_info{'memory'}) { + for (my $i = 0; $i < @dmidecode; $i++){ + if($dmidecode[$i] =~ /^\s*Memory Device/i) { + my $bank = $dmidecode[$i+9]; + $bank =~ /Bank Locator:\s+(.*)/; + $bank = $1; + next if !$bank; + $bank =~ s/\s//g; + $bank =~ s/[\s\.\/\\_]/-/g; + my $locator = $dmidecode[$i+8]; + $locator =~ /Locator:\s+(.*)/; + $locator = $1; + next if !$locator; + $locator =~ s/\s//g; + $locator =~ s![\s./\\_\#]!-!g; + my $size = $dmidecode[$i+5]; + $size =~ /Size:\s+(.*)/; + $size = $1; + next if !$size; + $size =~ s/\s*MB\s*//g; + chomp($size); + if ($bank . $locator ne "") { + $mainboard_memory_info{'memory'}{$bank . "-" . $locator} = $size; + } + } + } + } + $mainboard_memory_info{'mainboard'}{'manufacture'} =~ s/^\s+//; + $mainboard_memory_info{'mainboard'}{'name'} =~ s/^\s+//; + chomp($mainboard_memory_info{'mainboard'}{'manufacture'}); + chomp($mainboard_memory_info{'mainboard'}{'name'}); + } else { + $mainboard_memory_info{'mainboard'}{'manufacture'} = "dmidecode not installed"; + $mainboard_memory_info{'mainboard'}{'name'} = "dmidecode not installed"; + } + return %mainboard_memory_info; +} + +#sub has_raid { +# return ((`gmirror status` =~ /mirror/) || +# (`gstripe status` =~ /stripe/) || +# (grep {$_ =~ m/3w-xxxx: scsi|scsi. : Found a 3ware|3w-9xxx: scsi.: Found|LSISAS|LSILOGIC|Mylex AcceleRAID 160 PCI RAID Controller/i} @dmesg)); +#} + +sub get_disk_capacity { + my $device = shift; + chomp(my $capacity = `diskinfo $device | awk '{print \$3}' 2>\/dev\/null` || 0); + return normalize($capacity); +} + +sub get_hdd_info { + my %hdd_info; + my $disks = get_disks(); + + foreach my $disk (@$disks){ + if($disk =~ /^da/){ #SCSI + # TODO + # SCSI/SATA not supported for now under FreeBSD + next; +# # camcontrol devlist -v +# # dmesg | grep mpt +# +# # we don't wanna check raid volumes here +# next if grep {$_ =~ "^$disk at mpt"} @dmesg; +# $hdd_info{'model'}{$disk} = ''; + + } elsif($disk =~ /^ad/) { #IDE + # atacontrol list + chomp(my @ret = `atacontrol list`); + foreach(@ret){ + $hdd_info{'model'}{$disk} = $1 and last + if /\s*(?:Master|Slave):\s+$disk\s*<(.*?)>/; + } + } else { + next; + } + $hdd_info{'capacity'}{$disk} = get_disk_capacity("/dev/$disk"); + } + + return \%hdd_info; +} + +sub normalize { + my $bytes = shift || return 0; + my @units = qw/KB MB GB TB PB/; + my $i = -1; + + # if we get bytes/MB/TB and still want to normalize: + if($bytes =~ /^(\d+)\s*([a-zA-Z]+)\s*$/){ + $bytes = $1; + my $unit = $2; + foreach(@units){ + $i++; + last if uc($unit) eq $_; + } + } + return -1 if $bytes > 1024 and $i >= $#units; # error + + while($bytes > 1024){ + $i++; + $bytes = int($bytes/1024); + } + return $bytes.$units[$i]; +} + +sub get_disks { + chomp(my $d = `sysctl -n kern.disks 2>\/dev\/null`); + my @disks = split (/\s+/, $d); + return \@disks; +} + +send_mainboard_memory_info(); +send_hdd_info(); +EOF + chown 0:0 "$DIR_SCRIPTS_HOUR/hwinfo-root.pl" + chmod 750 "$DIR_SCRIPTS_HOUR/hwinfo-root.pl" +} + +# +# Generate /scripts/min/hddinfo.pl file +generate_hddinfo() { + echo "Generating $DIR_SCRIPTS_MIN/hddinfo.pl..." + cat << EOF > $DIR_SCRIPTS_MIN/hddinfo.pl +#! /usr/local/bin/perl +# version: $VERSION ($RELEASE_DATE) + +\$ENV{"LC_ALL"} = "POSIX"; + +EOF + cat <<'EOF' >> $DIR_SCRIPTS_MIN/hddinfo.pl +# +# This script provides simple disk tests and temperature +# + +use strict; + +# TODO: +# hddtemp for daX disk + +sub send_hdd_status { + my @ide; + chomp(my $i = `sysctl -n kern.disks 2>\/dev\/null`); + map { push @ide, $_ if /^ad/} split(/\s+/,$i); + + chomp(my @status = `dmesg 2>\/dev\/null \| grep -i \"error\\\|drive not ready\" \| grep -i \"\^ad\" \| cut -f 1 -d \":\" \| sort \| uniq`); + + foreach my $ide (@ide) { + my $error = 0; + foreach (@status) { + $error = 1 if $_ eq $ide; + } + if ($error == 1) { + print "mHW_HDD_$ide\_status|ERROR\n"; + } else { + print "mHW_HDD_$ide\_status|OK\n"; + } + } + + + # check of scsi errors + chomp(my @scsi = `sysctl -n kern.disks 2>\/dev\/null \| grep \"\^da\"`); + + my $possible_error; + if (scalar @scsi > 0) { + open my $dmesg, "dmesg |" or die "Can't launch dmesg: $!"; + my $status = 'OK'; + while (<$dmesg>) { + if (/Info fld=([^,]+), Deferred (\S+?): sense key (.+ Error)/) { + $status = $3; + } + if (/^da.: .+?: sense key: (.+ Error)/) { + $status = $1; + } + if (/^(da.+?): *rw=\d+/) { + $possible_error = $1; + next; + } + if (defined($possible_error) && /^attempt to access beyond/) { + $status = 'BAD_ACCESS'; + } + $possible_error = undef; + } + print "mHW_HDD_scsi_status|$status\n"; + } +} + +sub send_hdd_temp { + my %hdd_temp = get_hdd_temp(); + foreach (keys %hdd_temp) { + print "mINFO_HDD_$_\_temperature|" . $hdd_temp{$_} . "\n"; + } +} + + +sub get_hdd_temp { + my %hdd_temp = (); + chomp(my @ide = `sysctl -n kern.disks 2>\/dev\/null`); + + foreach (@ide) { + if (/^ad/) { + chomp(my $temp = `\/usr\/local\/sbin\/smartctl -a \/dev\/$_ \| grep "Temperature" 2>\/dev\/null`); + my $ide = $_; + if ($? == 0) { + if ($temp =~ m/\s+(\d{1,3})\s*$/) { + $temp = $1; + } else { + $temp = "-1"; + } + $hdd_temp{$ide} = $temp; + } else { + $hdd_temp{$ide} = "-2"; + } + } + elsif (/^da/) { + } + } + return %hdd_temp; +} + + +send_hdd_status(); +send_hdd_temp(); +EOF + chown 0:0 "$DIR_SCRIPTS_MIN/hddinfo.pl" + chmod 750 "$DIR_SCRIPTS_MIN/hddinfo.pl" +} + +# +# Generate rtm.pl file +generate_rtm() { + echo "Generating rtm.pl..." + cat << EOF > $RTM_PL +#! /usr/local/bin/perl +# version: $VERSION ($RELEASE_DATE) + +\$ENV{"LC_ALL"} = "POSIX"; + +EOF + cat <<'EOF' >> $RTM_PL +my $LF_DEFAULT = '/tmp/rtm.flock'; + +sub lockProcess { + my $lf = shift || $LF_DEFAULT || return; + my $pid = $$; + + if (-e "$lf") { + open(LOCKFILE, $lf) or die "Impossible to open lock file: $lf !!!"; + my $lockPID=<LOCKFILE> || ""; + close(LOCKFILE); + + if ($lockPID !~ m/^\d+$/ ) { + warn("There is no PID in lock. Something is broken..."); + exit 1; + } elsif (-e "/proc/$lockPID") { + exit 0; + } + warn("There is a lock file $lf, but no process for it. Overwritting lock file"); + } + + open(LOCKFILE, ">$lf") or die "Impossible to open lock file for writting: $lf !!!"; + print LOCKFILE $pid; + close(LOCKFILE); + + $LF_DEFAULT = $lf; +} + + +sub unlockProcess { + my $lf = shift || $LF_DEFAULT || return; + unlink($lf); +} + +use strict; +use Socket; +use Time::localtime; +use Symbol qw(gensym); +use IO::Select; +use POSIX qw(dup2); + +# Check for root permission +if ($) != 0) { + die "You are not a root!"; +} + +# Version of script +EOF + echo "my \$version = '$VERSION';" >> $RTM_PL + echo "my \$release_date = '$RELEASE_DATE';" >> $RTM_PL + cat <<'EOF' >> $RTM_PL + +# at this hour all information will be send +my $HOUR = 2; + +# get uptime +chomp(my $uptime = `\/sbin\/sysctl -n kern.boottime 2>\/dev\/null`); +chomp(my $date = `\/bin\/date +%s 2>\/dev\/null`); +$uptime =~ s/{\s+sec = (\d+),\s+.*$/$1/; +$uptime = $date - $uptime; +die "Can't get uptime!" + unless $uptime =~/^\d+$/; + + +my $script_name = $0; +# get basename of the script +$script_name =~ s/(^.*\/)//; + +EOF + echo "my \$base_dir = '$DIR';" >> $RTM_PL + echo "my \$scripts_dir_daily = '$DIR_SCRIPTS_DAILY';" >> $RTM_PL + echo "my \$scripts_dir_hour = '$DIR_SCRIPTS_HOUR';" >> $RTM_PL + echo "my \$scripts_dir_min = '$DIR_SCRIPTS_MIN';" >> $RTM_PL + echo "my \$rtm_update_ip = '$RTM_UPDATE_IP';" >> $RTM_PL + cat <<'EOF' >> $RTM_PL + +chomp(my @scripts_daily = `\/bin\/ls -1 $scripts_dir_daily`); +chomp(my @scripts_hour = `\/bin\/ls -1 $scripts_dir_hour`); +chomp(my @scripts_min = `\/bin\/ls -1 $scripts_dir_min`); + +my $env_path = $ENV{'PATH'}; +$ENV{'PATH'} = "/usr/local/sbin:/usr/local/bin:/root/bin:$env_path"; + +# global variable used to report errors from failed scripts +my $script_error = 0; + +# determine rtm server ip from mrtg config +my $ipfile = "$base_dir/etc/rtm-ip"; +open FP, "$ipfile" or die("failed to open '$ipfile' for reading: $!"); +chomp(my $destination_ip = <FP>); +close FP; +if ($destination_ip !~ /^\d+\.\d+\.\d+\.\d+$/) { + die "failed to read destination ip from '$ipfile': invalid ip: $destination_ip"; +} + +# lock file is defined with lockProcess() sub +lockProcess(); + +my $TIMEOUT = 45; + +my $tm = localtime(time); +my $hour = $tm->hour; +my $min = $tm->min; + +my @scripts_to_run = (); + +# per minute data +push @scripts_to_run, map { "$scripts_dir_min/$_" } @scripts_min; + +# hourly data +if (scalar @ARGV == 0 or $uptime < 900 or $ARGV[0] == $min) { + send_info("hINFO_uptime|" . $uptime); + push @scripts_to_run, map { "$scripts_dir_hour/$_" } @scripts_hour; +} + +# daily data +if (scalar @ARGV == 0 or $uptime < 900 or ($hour == $HOUR && $ARGV[0] == $min)) { + send_info("dINFO_RTM_version|" . $version); + push @scripts_to_run, map { "$scripts_dir_daily/$_" } @scripts_daily; +} + +# update rtm-ip daily +if (@ARGV > 0 && $hour eq $HOUR && $ARGV[0] == $min) { + system("$rtm_update_ip &"); +} + +# run collected scripts in separate processes +my $read_set = IO::Select->new(); +my %scripts_output = (); +foreach my $script (@scripts_to_run) { + my $P_STDOUT_READ = gensym(); + my $P_STDOUT_WRITE = gensym(); + my $P_STDERR_READ = gensym(); + my $P_STDERR_WRITE = gensym(); + pipe $P_STDOUT_READ, $P_STDOUT_WRITE or die "pipe(): $!"; + pipe $P_STDERR_READ, $P_STDERR_WRITE or die "pipe(): $!"; + my @stats = stat($script); + my $uid = $stats[4]; + + my $pid = fork(); + die "cannot fork: $!" if $pid < 0; + if ($pid == 0) { + if($uid > 0) { + my $gid = $stats[5]; + drop_priv($uid, $gid) + } + + dup2(fileno($P_STDOUT_WRITE), 1) or die "dup2(): $!"; + dup2(fileno($P_STDERR_WRITE), 2) or die "dup2(): $!"; + close $P_STDOUT_READ; + close $P_STDERR_READ; + close $P_STDOUT_WRITE; + close $P_STDERR_WRITE; + my $error = "timeout"; + my $ok = eval { + local $SIG{ALRM} = sub { die; }; + alarm($TIMEOUT); + system($script); + if ($? == -1) { + $error = "failed to execute '$script': $!"; + die; + } + }; + if (!defined($ok)) { + print STDERR "$error"; + exit 1; + } + exit; + } + close $P_STDOUT_WRITE; + close $P_STDERR_WRITE; + $read_set->add($P_STDOUT_READ); + $read_set->add($P_STDERR_READ); + $scripts_output{$pid} = { 'script' => $script, + 'stdout' => $P_STDOUT_READ, + 'stderr' => $P_STDERR_READ, + 'error' => [], + }; +} + +# wait for all scripts to complete +while (my @fds = $read_set->can_read()) { + foreach my $fd (@fds) { + my $slot; + foreach my $s (values %scripts_output) { + if ($s->{'stdout'} == $fd || $s->{'stderr'} == $fd) { + $slot = $s; + last; + } + } + unless (defined($slot)) { + warn "FATAL: got event on unknown file descriptor!"; + $read_set->remove($fd); + close $fd; + next; + } + my $line = <$fd>; + if (!$line) { + $read_set->remove($fd); + close $fd; + next; + } + chomp($line); + if ($fd == $slot->{'stderr'}) { + push @{$slot->{'error'}}, $line; + print STDERR "$line\n"; + } else { + send_info($line); + } + } +} +while (1) { + my $pid = waitpid(-1, 0); + last unless $pid > 0; + $scripts_output{$pid}->{'status'} = $? >> 8; +} + +# find scripts which returned error +foreach my $slot (values %scripts_output) { + next if $slot->{'status'} == 0; + $slot->{'script'} =~ m!/([^/]+?)$!; + my $script_name = $1; + my $stderr = join ' ', map { chomp; $_ } @{$slot->{'error'}}; # perl sucks + if (length $stderr > 20) { + $stderr = substr($stderr, 0, 150); + $stderr .= '...'; + } + chomp($stderr); + $script_error = "1 $script_name $stderr"; + # TODO: it currently sends errors for the first failed script + last; +} + +send_info("mINFO_RTM_status|$script_error"); + +unlockProcess(); +exit 0; + + +sub send_info { + my $message = shift; + + my $port = 6100 + int(rand(100)); + + my $ok = eval { + local $SIG{ALRM} = sub { print "rtm timeout\n"; die; }; + alarm(10); + + my $proto = getprotobyname('udp'); + socket(Socket_Handle, PF_INET, SOCK_DGRAM, $proto); + my $iaddr = gethostbyname($destination_ip); + my $sin = sockaddr_in("$port", $iaddr); + send(Socket_Handle, "rtm $message", 10, $sin); + print "rtm $message\n"; + alarm(0); + }; + if (!defined($ok)) { + $script_error = "1 send_info() rtm timeout"; + warn "error: $@\n"; + } +} + +sub drop_priv { + my ($uid, $gid) = @_; + + # set EGID + $) = "$gid $gid"; + # set EUID + $> = $uid + 0; + if ($> != $uid) { + die "Can't drop EUID."; + } +} +EOF + chown 0:0 "$RTM_PL" + chmod 750 "$RTM_PL" +} + +# ^L +# Generate rtm-update-ip.sh file +generate_rtm_update_ip() { + echo "Generating rtm-update-ip.sh..." + cat << EOF > $RTM_UPDATE_IP.tmp +#! /bin/sh +# version: $VERSION ($RELEASE_DATE) + +LC_ALL=POSIX + +EOF + cat <<'EOF' >> $RTM_UPDATE_IP.tmp +EOF + echo "DIR='$DIR'" >> $RTM_UPDATE_IP.tmp + cat <<'EOF' >> $RTM_UPDATE_IP.tmp + +get_interface_ip() { + iface=$1 + ifconfig $iface 2>\/dev\/null | grep "inet.*netmask" | awk '{print $2}' | egrep '[0-9]+(\.[0-9]+){3}' +} + +mainif="$(netstat -rn|grep "^default" | awk '{print $NF}'|sort|uniq|head -n1)" +mainip="$(get_interface_ip $mainif)" + +arpa=`echo "$mainip" | sed "s/\./ /g" | awk '{print $3"."$2"."$1}'`; +ip=`host -t A mrtg.$arpa.in-addr.arpa $DNSSERVER 2>/dev/null | tail -n 1 | sed -ne 's/.*[\t ]\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p'` +if [ -z "$ip" ]; then + echo "No IP from OVH network !" + exit 1; +fi +echo $ip > "$DIR/etc/rtm-ip" + +EOF + chown 0:0 "$RTM_UPDATE_IP.tmp" + chmod 750 "$RTM_UPDATE_IP.tmp" + mv "$RTM_UPDATE_IP.tmp" "$RTM_UPDATE_IP" + test -f $DIR/bin/rtm-update-ip.pl && rm $DIR/bin/rtm-update-ip.pl + $RTM_UPDATE_IP +} + + +lock_process() { + if [ -e "$lockfile" ]; then + lockpid=`cat "$lockfile"` + if [ -e "/proc/$lockpid" ]; then + echo "there seems to be stale $scriptname process running. check \"ps aux | grep $scriptname\"" >&2 + exit 1; + fi + echo "Lock $lockfile with pid $lockpid exist for script $scriptname but no process not exist, replaced by new one!!" >&2 + echo $$ > "$lockfile" + fi + echo $$ > "$lockfile.$$" + mv "$lockfile.$$" "$lockfile" +} + +unlock_process() { + rm -f "$lockfile" +} + +wait_for_lock() { + echo "Waiting for finish rtm running from CRON" + for i in `\/usr\/bin\/jot - 1 30`; do + if [ -e "$lockfile" ]; then + echo -n "." + sleep 2 + else + echo -e "\nFinished." + break + fi + done +} + + +# +# RTM installation code + +lockfile='/tmp/rtm.flock' +if [ -e "$lockfile" ]; then + wait_for_lock +fi +lock_process + +# Generate selected scripts +for script in $SCRIPTS_TO_INSTALL; do + generate_$script +done +generate_rtm_update_ip +generate_rtm + +unlock_process + +if [ -e "$RTM_SH" ];then + mv $RTM_SH $RTM_SH.old +fi +ln -s $RTM_PL $RTM_SH + +rm -rf /rpms + +CRONTAB=/etc/crontab +if [ -z "`cat $CRONTAB | grep \"$RTM_SH\"`" ]; then + minute=`perl -e 'print ((rand()*100)%60)'` + CRONTABLINE='*/1 * * * * root '$RTM_SH' '$minute' > /dev/null 2> /dev/null' + echo "$CRONTABLINE" >> $CRONTAB +else + perl -pe 's!>/dev/null!> /dev/null!g' -i "$CRONTAB" +fi + +# List entries in crontab +echo "" +echo "Crontab entries:" +cat $CRONTAB +sleep 2 +echo "" +# restarting CRON +for i in `jot 5 1` ; do + echo "Restarting CRON. Try $i" + if [ -x "/etc/rc.d/crond" ];then + killall -9 crond + $SCREEN /etc/rc.d/init.d/crond restart + elif [ -x "/etc/rc.d/cron" ];then + killall -9 cron + $SCREEN /etc/rc.d/cron restart + else + echo "WARNING: Didn't find any method of restarting cron on your distribution!" >&2 + fi + sleep 2 + if [ -z "`ps aux | grep -v grep |grep cron`" ]; then + echo "Cron didn't start." + else + break + fi +done + +if [ -z "`ps aux | grep -v grep | grep cron`" ]; then + echo "Please check it!" + sleep 10 +else + echo "CRON restarted succefully." + sleep 2 +fi + +echo "" +echo "in $DIR_SCRIPTS_MIN/check.pl you can add more things to check (monitors):" +echo "when everything is fine:" +echo "CHECK_vm|" +echo "CHECK_oops|" +echo "on breakdown:" +echo "CHECK_vm|1" +echo "CHECK_oops|1" +echo +echo "for exemple:" +echo "# $DIR_SCRIPTS_MIN/check.pl" +$DIR_SCRIPTS_MIN/check.pl +echo "" +echo "Sending all informations:" + +$RTM_SH + +# Local variables: +# page-delimiter: "^# *\f" +# sh-basic-offset: 4 +# End: + |