Monday, October 17, 2005

[UNIX] Daily Plot - Solaris SAR data using gnuplot

#! /bin/sh
#
# For Solaris, 
# generate daily sar graph for cpu and freemem (normalised to 100%)
# run this using cron at 23:59

 
 
#
# prepare environment variables
#
PATH=/usr/bin:/usr/sbin:/bin:/apps/usr/local/bin
export PATH
LD_LIBRARY_PATH=/usr/lib:/apps/usr/local/lib
export LD_LIBRARY_PATH
 
 
#
# local variables
#
TEMP_USR=/tmp/sar-usr.$$
TEMP_MEM=/tmp/sar-mem.$$
TEMP_GP=/tmp/sar-gp.$$
#
PNGDIR="/spare/var/adm/sa/`date '+%Y-%m'`"
PNG="$PNGDIR/sar-`date '+%Y-%m-%d'`.png"
if [ ! -d $PNGDIR ]; then
        mkdir -p $PNGDIR
        chown adm:sys $PNGDIR
        chmod g+w $PNGDIR
fi
 
 
#
# save SAR cpu and freemem data
#
sar -u > $TEMP_USR
sar -r > $TEMP_MEM
 
 
#
# convert free memory (pages) to percentage
#
pagesize=`pagesize`
memory=`prtconf | awk '/^Memory/ {print $3}'`
scale=`echo "scale=10; $pagesize*100/(1024*1024*$memory)" | bc -l`
 
 
 
#
# prepare gnuplot file to generate png
# cpu utilisation and freemem are plot as percentage
# fremem is based on 4096MB memory and pagesize of 8192
#
echo "
set terminal png color
set output '$PNG'
set xdata time
set timefmt '%H:%M:%S'
set xrange ['00:00:00':'23:59:59']
set yrange [0:100]
set xlabel 'Hour'
set ylabel 'Percentage'
set xtics '00:00:00', 3600, '23:59:59'
set format x '%H'
set nomxtics
set grid
set size 1,0.5
set key right outside
set timestamp bottom
set title 'CPU/Memory Utilisation'
plot '$TEMP_USR' using 1:(\$2+\$3) title 'CPU (usr+sys)' with linespoints, \     '$TEMP_MEM' using 1:(\$2*$scale) title 'Free memory' with linespoints
" > $TEMP_GP
gnuplot $TEMP_GP
 
 
#
# cleanup
#
rm -f $TEMP_USR $TEMP_MEM $TEMP_GP

Friday, October 14, 2005

[Tcl] Remote Monitoring (rstat)

#! /usr/local/bin/tclsh
#
# On Solaris, rstat requires /etc/rc2.d/S71rpc to start and rstat in /etc/inetd.conf
# to be enabled
#
#




if { $argc < 1 || $argc > 3 } {
 puts "Usage: $argv0  \[interval\] \[iteration\]\n"
 puts {       interval [60]}
 puts {       iteration [10]}
 exit
}
set host [lindex $argv 0]
set interval [lindex $argv 1]
set iteration [lindex $argv 2]
if { [string length $interval] == 0 } {
 set interval 60
}
if { [string length $iteration] == 0 } {
 set iteration 10
}


#
# get pagesize
#set pagesize [exec pagesize]
set pagesize 8192


package require Tnm
namespace import Tnm::*


proc Sunrpc { host } {
 global old new
 global pagesize

 set new [join [sunrpc stat $host]]
 foreach { n t v } $old { set s1($n) $v }
 foreach { n t v } $new { set s2($n) $v }
 set delta [expr $s2(curtime) - $s1(curtime)] 
 foreach { n1 t1 v1 } $old { n2 t2 v2 } $new {
  switch $t1 {
   Counter {
    set r($n1) [expr $delta > 0 ?      int(double($v2-$v1)/$delta) : 0]
   }
   Gauge {
    set r($n1) [expr $v2/256.0]
   }
   default {
    set r($n1) $v2
   }
  }
 }

 set f [expr $pagesize/1024]
 puts [format {%s  %3d %3d %3d %3d  %4d %4d %4d %4d  %4d %4d  %6.2f %6.2f %6.2f}   [clock format $r(curtime) -format {%H:%M:%S}]   $r(cp_user) $r(cp_system) $r(cp_nice) $r(cp_idle)   [expr $r(v_pgpgin)*$f] [expr $r(v_pgpgout)*$f]   [expr $r(v_pswpin)*$f] [expr $r(v_pswpout)*$f]   $r(v_intr) $r(v_swtch)   $r(avenrun_0) $r(avenrun_1) $r(avenrun_2)]


 set old $new
}



#
# calculate time, uptime
#
set old [join [sunrpc stat $host]]
foreach { n t v } $old {
 set initial($n) $v
}
set curtime [clock format $initial(curtime) -format {%H:%M%p}]
set delta [expr $initial(curtime) - $initial(boottime)]
set day [expr $delta/86400]
set hr  [expr ($delta-$day*86400)/3600]
set min [expr ($delta-$day*86400-$hr*3600)/60]
puts "uptime:"
puts " $curtime  up  $day day(s), $hr:$min\n\n"


#     12345678901234567890123456789012345678901234567890123456789012345678901234567890
#     HH:MM:SS
puts "                cpu          page(kB)  swap(kB)                 run queue"
puts "          %us %sy %wt %id    pi   po   si   so  intr  csw   1-min  5-min 15-min"


after [expr $interval*1000]
job create -interval [expr $interval*1000]  -command [list Sunrpc $host]  -iterations $iteration  -exit { set forever {} }


vwait forever



# sample output
#
# ./rstat.tcl 192.168.0.1 5 10
# uptime:
#  14:12PM  up  36 day(s), 2:36
#  
#  
#                 cpu          page(kB)  swap(kB)                 run queue
#           %us %sy %wt %id    pi   po   si   so  intr  csw   1-min  5-min 15-min
# 14:12:26    0   1   0 399     0    0    0    0   270  248    0.02   0.02   0.02
# 14:12:31    0   1   0 398     0    0    0    0   269  241    0.02   0.02   0.02
# 14:12:36    0   5   0 394     0    0    0    0   275  256    0.02   0.02   0.02
# 14:12:41    0   1   0 398     0    0    0    0   276  263    0.02   0.02   0.02
# 14:12:46    0   0   0 398     0    0    0    0   281  253    0.02   0.02   0.02
# 14:12:51    0   1   0 398     0    0    0    0   274  253    0.02   0.02   0.02
# 14:12:56    0   0   0 398     0    0    0    0   274  245    0.02   0.02   0.02
# 14:13:01    0   1   0 397     0    0    0    0   270  246    0.02   0.02   0.02
# 14:13:06    0   4   0 395     0    0    0    0   272  241    0.02   0.02   0.02
# 14:13:11    0   1   0 398     0    0    0    0   268  253    0.02   0.02   0.02

[Tcl] Network Connection Statistics

#! /usr/local/bin/tclsh
#
# find out netstat states via SNMP
#
#

if { $argc < 1 || $argc > 3 } {
 puts "Usage: $argv0  \[interval\] \[iteration\]\n"
 puts {       interval [60]}
 puts {       iteration [10]}
 exit
}


package require Tnm
namespace import Tnm::*


set host [lindex $argv 0]
set interval [lindex $argv 1]
set iteration [lindex $argv 2]
if { [string length $interval] == 0 } {
 set interval 60
}
if { [string length $iteration] == 0 } {
 set iteration 10
}


set snmp [snmp generator -address $host -timeout 1 -retries 1]
if { [catch {$snmp get sysUpTime.0}] != 0 } {
 puts "Error. SNMP service not available at \"$host\"\n"
 exit 2
}


proc getServices { {f /etc/services} } {
 global services

 set fp [open $f r] 
 while { [gets $fp line] >= 0 } {
  if { [regexp {(^\s*$)|(^\s*#)} $line] } {
   continue
  }

  if { [regexp {^\s*(\w+)\s+(\d+)/tcp} $line dummy name port] } {
   set services($port) $name
  }
 }
 close $fp
}


proc getTcpListen { snmp } {
 set dot {.}
 set rc {}
 $snmp walk x tcpConnState {
  set oid [snmp oid $x]
  set val [snmp value $x]
  if { [string equal listen $val] } {
   set loid [split $oid $dot]
   lappend rc [join [lrange $loid 14 14] $dot]
  }
 }
 return $rc
}


proc printTcpListen { listen } {
 global services

 set txt {}
 set count 0
 foreach port [lsort -integer $listen] {
  incr count
  if { [info exists services($port)] } {
   append txt "$port ($services($port))\n"
  } else {
   append txt  "$port\n"
  }
 }
 return $txt
}


proc getTcpState { snmp } {

 set dot {.}

 # Tnm::mib oid tcpConnState
 # 1.3.6.1.2.1.6.13.1.1
 $snmp walk x tcpConnState {
  set oid [snmp oid $x]
  set val [snmp value $x]
  set loid [split $oid $dot]
  set lip [join [lrange $loid 10 13] $dot]
  set rip [join [lrange $loid 15 18] $dot]

  # local <-> remote
  if { ![string equal $lip $rip] } {
   if { [info exists rc($val)] } {
    incr rc($val)
   } else {
    set rc($val) 1
   }
  }
 }

 return [array get rc]
}


proc printTcpState { snmp } {
 set rc {}
 foreach { s v } [getTcpState $snmp] { lappend rc [list $s $v] }

 puts -nonewline "[clock format [clock seconds] -format {%H:%M:%S}]  "
 foreach i [lsort -index 0 -dictionary $rc] {
  puts -nonewline "[lindex $i 0] ([lindex $i 1])  "
 }
 puts ""
}


getServices port.txt

puts "TCP Listening ports:"
puts [printTcpListen [getTcpListen $snmp]]

puts "\n\nTCP Connection State:"
job create -interval [expr $interval*1000]  -command "printTcpState $snmp"  -iterations $iteration  -exit { set forever {} }


vwait forever





# Sample output
# ./netstat.tcl 192.168.0.1 5 10
# TCP Listening ports:
# 21 (ftp)
# 22 (SSH_Remote_Login_Protocol)
# 23 (telnet)
# 79 (finger)
# 80 (http)
# 111 (sunrpc)
# 513 (login)
# 514 (shell)
# 536
# 898
# 3306 (MySQL)
# 4045 (lockd)
# 5987
# 5988
# 7100 (fs)
# 8005
# 8009
# 8080 (HTTP)
# 32771 (Sun_RPC)
# 32772
# 32773
# 32774
# 32778
# 32779
# 32780 (RPC)
# 32781
# 32782
#  
#  
#  
# TCP Connection State:
# 13:49:15  established (17)  listen (1)  timeWait (4)
# 13:49:20  established (20)  listen (1)  timeWait (4)
# 13:49:25  established (20)  listen (1)  timeWait (4)
# 13:49:30  established (21)  listen (1)  timeWait (4)
# 13:49:35  established (21)  listen (1)  timeWait (4)
# 13:49:40  established (21)  listen (1)  timeWait (4)
# 13:49:45  established (21)  listen (1)  timeWait (4)
# 13:49:50  established (21)  listen (1)
# 13:49:55  established (17)  finWait2 (4)  listen (1)
# 13:50:00  established (17)  listen (1)  timeWait (4)

[UNIX] Process Tree

#! /bin/sh

# List all processes in tree format (show dependency)

this=$$
ps -ef | awk -v this=$pid '
 function ptree(myid,space,  lpid) {
  split(p2c[myid],lpid,",")
  for(i in lpid) {
   id=lpid[i]
   print space, id, cmd[id]
   spc=sprintf("%s%s",space,"  ")
   ptree(id,spc);
  }
 }

        NR>1 {
  n=length($0); process=substr($0,48,n-48+1)
                pid=$2
                ppid=$3
  if ( pid == this || ppid == this ) {
   next
  }
                c2p[$2]=$3
  cmd[$2]=process
  if ( length(p2c[$3]) == 0 ) {
   p2c[$3]=$2
  } else {
                 p2c[$3]=sprintf("%s,%s",p2c[$3],$2)
  }
 } 
 END {
  ptree(1,"")
 }'

[Tcl] Send SMS to Singtel customer via command line

#! /usr/local/bin/wish

#
# Send SMS via the SingTel web page by cracking their
# spamcode.
#   step1: visit send sms page to get valid cookies
#   step2: extract the spamcode image url and retrieve
#   step3: send sms with the codes and crack spamcode
#
# Note: you need X windows
#



if { $argc < 2 } {
 puts stderr "Usage: $argv0 handphone message ..."
 exit 1
}
set handphone [lindex $argv 0]
set message [string range $argv [string first [lindex $argv 1] $argv] end]
if { ![regexp {^9\d{7}$} $handphone] } {
 puts stderr "Error. Invalid handphone number"
 exit 2
}


package require http
wm withdraw .


#
# url
#
set host 165.21.20.138
set url1 "http://$host/consumer/msg_center/internet_sms.asp"
set url2 "http://$host/consumer/msg_center/"
set url3 "http://$host/consumer/msg_center/internet_sms_process.asp"


#
# look like a browser
#
http::config  -useragent {Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.0.1) Gecko/20020920 Netscape/7.0}  -accept {text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,text/css,*/*;q=0.1}


#
# http headers
#
set headers [list  Accept-Language {en-us, en;q=0.50}  Accept-Encoding {gzip, deflate, compress;q=0.9}  Accept-Charset {ISO-8859-1, utf-8;q=0.66, *;q=0.66}  Keep-Alive {300}  Connection {close} ]

 

#
# finding SITEID and ASSPSESSIONID cookies
#
set s [http::geturl $url1 -headers $headers]
set d [http::data $s]
set ck [lsearch [set ${s}(meta)] {Set-Cookie}]
set cookie1 [lindex [split [lindex [set ${s}(meta)] [expr $ck + 1]] {;}] 0]
set lmeta [lrange [set ${s}(meta)] [expr $ck + 1] end]
set ck [lsearch $lmeta {Set-Cookie}]
set cookie2 [lindex [split [lindex $lmeta [expr $ck + 1]] {;}] 0]
http::cleanup $s


#
# find the spamcode url
#
set rc [regexp {Your SMS ID is \(<img src='([^']+)'>} $d dummy spamcode]
if { $rc != 1 } {
 puts "Error. Unable to extract the spamcode url. Abort"
 exit 1
}
set url2 "$url2$spamcode"


#
# get spamcode image
#
set tmpfile ".tmp[pid].gif"
set fp [open $tmpfile w]
fconfigure $fp -translation binary
lappend headers Cookie $cookie1 Cookie $cookie2
set s [http::geturl $url2 -channel $fp -headers $headers]
close $fp


#
# decode gif image to numbers
#
set i [image create photo -file $tmpfile]
set idata [$i data -background #ff0000]
 #
 # spamcode Nxx
 #
 set nxx {}
 for {set j 0 } { $j < 9 } { incr j } {
  append nxx [join [lrange [lindex $idata $j] 1 5] {}]
 }
 regsub -all {#ff0000} $nxx {o} a
 regsub -all {#000000} $a {i} b
 set nxx $b

 #
 # spamcode xNx
 #
 set xnx {}
 for {set j 0 } { $j < 9 } { incr j } {
  append xnx [join [lrange [lindex $idata $j] 8 12] {}]
 }
 regsub -all {#ff0000} $xnx {o} a
 regsub -all {#000000} $a {i} b
 set xnx $b

 #
 # spamcode xxN
 #
 set xxn {}
 for {set j 0 } { $j < 9 } { incr j } {
  append xxn [join [lrange [lindex $idata $j] 15 19] {}]
 }
 regsub -all {#ff0000} $xxn {o} a
 regsub -all {#000000} $a {i} b
 set xxn $b

 #
 # image to number mapping
 #
 set code(0) oiiioioooiioooiioooiioooiioooiioooiioooioiiio
 set code(1) oiiioioooiooooiooooiooiioooooiooooiioooioiiio
 set code(2) oiiioioooiooooiooooioooiooooioooioooioooiiiii
 set code(3) oiiioioooiooooiooooiooiioooooiooooiioooioiiio
 set code(4) oooioooiioooiiooioiooioioiooioiiiiioooiooooio
 set code(5) oiiiioioooiooooiiiioioooiooooiooooiioooioiiio
 set code(6) oiiioioooiiooooioiioiiooiioooiioooiioooioiiio
 set code(7) iiiiioooiooooioooiooooiooooioooiooooiooooiooo
 set code(8) oiiioioooiioooiioooioiiioioooiioooiioooioiiio
 set code(9) oiiioioooiioooiioooiiooiioiioiooooiioooioiiio

 #
 # matching
 #
 for { set j 0 } { $j <= 9 } { incr j } {
  if { [string equal $nxx $code($j)] == 1 } { set a $j }
  if { [string equal $xnx $code($j)] == 1 } { set b $j }
  if { [string equal $xxn $code($j)] == 1 } { set c $j }
 }


#
# send sms message
#
set q [http::formatQuery  mobile_no $handphone  typed_spam_code $a$b$c  message "$message"  charstyped [llength $message] ]
set s [http::geturl $url3 -query $q -headers $headers]
http::cleanup $s


#
# cleaning up
#
file delete -force $tmpfile



exit

[UNIX] Solaris 10 Package Lists

#! /bin/sh
#
# create package lists from cluster table of content
# for solaris 10
#
# see Solaris 10 package list
# http://docs.sun.com/app/docs/doc/817-0545/6mgbberia?a=view
#


TOC=/var/sadm/system/admin/.clustertoc
if [ ! -f $TOC ]; then
 echo "CLuster toc cannot be found at $TOC\n"
 exit 1
fi



TMPFILE=.tmp_$$


awk '
BEGIN {
 dist[1]="SUNWCrnet";  # reduced networking
 dist[2]="SUNWCreq";  # core
 dist[3]="SUNWCuser";  # end user
 dist[4]="SUNWCprog";  # developer
 dist[5]="SUNWCall";  # entire distribution
 dist[6]="SUNWCXall";  # entire distribution (plus OEM)
 comma=","
}
{
 split($0,var,"=")
 if ( var[1] ~ /CLUSTER$/  ) { 
  cluster=var[2]
  p2c[cluster]=""
 }

 # setup child and parent relationship
 if ( var[1] == "SUNW_CSRMEMBER" ) {
  child=var[2]

  # avoid duplicate
  if ( c2p[child] == cluster ) {
   next
  }

  c2p[child]=cluster
  if ( p2c[cluster] == "" ) {
   p2c[cluster]=child
  } else {
   p2c[cluster]=sprintf("%s%s%s",p2c[cluster],comma,child)
  }
 }
}
END {
 for ( i=1 ; i<=5 ; ++i ) {

  aCluster=dist[i]
  split(p2c[aCluster],svar1,comma)
  for ( j in svar1 ) {
   aChild=svar1[j]

   # if child is cluster, go one level down
   if ( aChild ~ /^SUNWC/ ) {
    split(p2c[aChild],svar2,comma)
    for ( k in svar2 ) {
     aGrandChild=svar2[k]
     printf("%s:%s:%s\n",aCluster,aChild,aGrandChild)
    }
   } else {
    printf("%s:%s\n",aCluster,aChild)
   }
  }
 }
}' $TOC > $TMPFILE


#
# each cluster distribution file will contain all the package names
#
for c in SUNWCrnet SUNWCreq SUNWCuser SUNWCprog SUNWCall SUNWCXall
do
 egrep "^$c" $TMPFILE | awk -F: '{print $NF}' | sort > $c
done
rm -r $TMPFILE



tree() 
{
 # big is the superset cluster
 # small is the immediate subset of the 'big'
 big=$1
 small=$2
 level=$3

 # work out the indentation
 level=`expr $level - 1`
 while [ $level -gt 0 ]; do
  echo "  \c"
  level=`expr $level - 1`
 done

 # create 'pkginfo' database for output reference
 # diff between superset cluster and subset cluster
 echo "[$big]"
 (echo "DB_START" ; pkginfo ; echo "DB_END" ; diff $big $small | egrep "^< ") | nawk -v level=$3 '
  /DB_START/,/DB_END/ {
          if ( NF >= 3 ) {
                  pkg=$2
                  desc=$3
                  for(i=4;i<=NF;++i) {
                          desc=sprintf("%s %s",desc,$i)
                  }
                  db[pkg]=desc
   }
   next
         }
  {
   # indentation for level
   ident=""
   for ( i=0;i< level;++i ) { 
    ident="  "ident
   }

   # ensure description having same indentation
   nspace=25-length($2)-length(ident)
   space=""
   for ( i=1;i<=nspace;++i ) { 
    space=" "space
   }
   printf("%s%s%s/* %s */\n",ident,$2,space,db[$2])
  }'
}

depend="Dependency"
tree SUNWCXall SUNWCall  1 >  $depend
tree SUNWCall  SUNWCprog 2 >> $depend
tree SUNWCprog SUNWCuser 3 >> $depend
tree SUNWCuser SUNWCreq  4 >> $depend
tree SUNWCreq  SUNWCrnet  5 >> $depend
tree SUNWCrnet   /dev/null 6 >> $depend

Thursday, October 13, 2005

[UNIX] Most Efficiency LD_LIBRARY_PATH

#! /bin/sh
                                                                                                                             
# Generate the most efficient LD_LIBRARY_PATH
#
# find out the frequency of the dynamic sharable library path and arrange
# it such that the mostly used path comes first
                                                                                                                             
if [ $# -ne 1 ]; then
        echo "Usage: $0 " 1>&2
        exit 1
fi
if [ ! -f $1 ]; then
        echo "Error. Cannot find $1" 1>&2
        exit 2
fi
                                                                                                                             
                                                                                                                             
for i in `ldd $1 | awk '{print $NF}'`
do
        echo `dirname $i`
done | sort | uniq -c | sort -k1,1 -n -r | awk ' BEGIN { printf("LD_LIBRARY_PATH=") }
            { printf("%s:",$2) }
      END { printf("\nexport LD_LIBRARY_PATH\n") }'

Thursday, October 06, 2005

[UNIX] seq (in Linux) equivalent

#
# Bourne shell functions similar to 'seq' in linux, except without step
# you can use it to do this in a for loop
# for i in `range 1 100`
# do
#    do-something
# done
#
range1()
{
 start=$1
 end=$2
 count=$start
 rc=""
 while [ $count -le $end ]
 do
  rc="$rc $count"
  count=`expr $count + 1`
 done
 echo $rc
}
range2()
{
 perl -e "\$,=\" \"; print $1..$2;"
}
range3()
{
 gawk -v s=$1 -v e=$2 'END{for(i=s;i<=e;++i) {printf("%d ",i)}}' /dev/null
}
range4()
{
 echo "for {set i $1} {\$i<=$2} {incr i} {puts -nonewline \"\$i \"}" | tclsh
}
range5()
{
 yes | pr -f -n | head -`expr $2 + 3` | tail -`expr $2 - $1 + 1` | cut -c1-5
}