From a8c84d2892f07a6863b68a11eb0a4a79ffd71fb5 Mon Sep 17 00:00:00 2001 From: Andrew DeFaria Date: Tue, 12 Jul 2022 08:55:58 -0700 Subject: [PATCH] Added client work scripts --- clients/Ameriquest/bin/PMO-CM.cmd | 13 + clients/Ameriquest/bin/backup.pl | 251 + clients/Ameriquest/bin/ccase_lock_vobs.bat | 9 + clients/Ameriquest/bin/ccase_unlock_vobs.bat | 9 + clients/Ameriquest/bin/ccverify.pl | 152 + clients/Ameriquest/bin/cmverify.cmd | 53 + clients/Ameriquest/bin/cqverify.pl | 74 + clients/Ameriquest/bin/getccgroups.vbs | 215 + clients/Ameriquest/bin/tagit.pl | 183 + clients/Ameriquest/bin/vobsize.pl | 72 + clients/Ameriquest/bin/whosin.cmd | 3 + clients/Ameriquest/bin/whosin.vbs | 169 + clients/Ameriquest/triggers/ASAPNotify.msg | 16 + .../triggers/ASAP_BUS_REQ_Notify.msg | 16 + clients/Ameriquest/triggers/AddExecute.pl | 24 + clients/Ameriquest/triggers/CommentSQLCode.pl | 153 + clients/Ameriquest/triggers/LogActivity.pl | 198 + clients/Ameriquest/triggers/NoPBLs.pl | 65 + clients/Ameriquest/triggers/Permissions.pl | 228 + .../Ameriquest/triggers/defaria_notify.msg | 16 + clients/GD/FSMon/Filesystem.pm | 324 + clients/GD/FSMon/Fonts/GeosansLight.ttf | Bin 0 -> 60072 bytes clients/GD/FSMon/Fonts/MankSans.ttf | Bin 0 -> 58492 bytes clients/GD/FSMon/Fonts/Silkscreen.ttf | Bin 0 -> 16172 bytes clients/GD/FSMon/Fonts/pf_arma_five.ttf | Bin 0 -> 21936 bytes clients/GD/FSMon/Fonts/tahoma.ttf | Bin 0 -> 383804 bytes clients/GD/FSMon/FsmonDB.php | 181 + clients/GD/FSMon/FsmonDB.pm | 700 ++ clients/GD/FSMon/FsmonDB.sql | 81 + clients/GD/FSMon/Fsutils.php | 105 + clients/GD/FSMon/fsmon | 83 + clients/GD/FSMon/fsmon.php | 233 + clients/GD/FSMon/graphFS.php | 174 + clients/GD/FSMon/pChart/pCache.class | 119 + clients/GD/FSMon/pChart/pChart.class | 2883 ++++++++ clients/GD/FSMon/pChart/pData.class | 260 + clients/GD/FSMon/testgraph.php | 62 + clients/GD/cqtool.tar.gz | Bin 0 -> 10457 bytes clients/GD/cqtool/CreateHelpDeskUI.pm | 627 ++ clients/GD/cqtool/CreateWORUI.pm | 575 ++ clients/GD/cqtool/cqtool.pl | 780 +++ clients/GD/rexec | 329 + clients/HP/bin/SCS | 878 +++ clients/HP/bin/add_email | 182 + clients/HP/bin/add_moa | 184 + clients/HP/bin/add_postnote | 182 + clients/HP/bin/add_sharedx | 187 + clients/HP/bin/add_synchronize | 195 + clients/HP/bin/add_user | 497 ++ clients/HP/bin/adl-config | 437 ++ clients/HP/bin/adl-config.src | 163 + clients/HP/bin/allmach | 111 + clients/HP/bin/arcserverenv | 66 + clients/HP/bin/atria.scs | 727 ++ clients/HP/bin/bigfiles | 109 + clients/HP/bin/buildservers | 39 + clients/HP/bin/check_security | 93 + clients/HP/bin/cleantmps | 59 + clients/HP/bin/config_disk_array | 189 + clients/HP/bin/configure_machine | 325 + clients/HP/bin/cpucount | 34 + clients/HP/bin/cygwin_setup | 241 + clients/HP/bin/daily | 151 + clients/HP/bin/desktopmach | 60 + clients/HP/bin/diskspace | 112 + clients/HP/bin/display_path | 32 + clients/HP/bin/dum | 19 + clients/HP/bin/fix_automounts | 91 + clients/HP/bin/fix_startup | 53 + clients/HP/bin/fixlocalaliases | 17 + clients/HP/bin/fixntp | 43 + clients/HP/bin/fixrhosts | 65 + clients/HP/bin/fk1 | 98 + clients/HP/bin/fk2 | 98 + clients/HP/bin/fk3 | 98 + clients/HP/bin/fk4 | 98 + clients/HP/bin/fk5 | 98 + clients/HP/bin/fk6 | 98 + clients/HP/bin/get_info | 257 + clients/HP/bin/infrastructuremach | 60 + clients/HP/bin/insert | 522 ++ clients/HP/bin/install_new_kernel | 65 + clients/HP/bin/install_pwplus | 28 + clients/HP/bin/inventory | 310 + clients/HP/bin/lpsetup | 156 + clients/HP/bin/lspatches | 28 + clients/HP/bin/lsproduct | 25 + clients/HP/bin/lvs | 33 + clients/HP/bin/machine_info | 84 + clients/HP/bin/machine_stats | 131 + clients/HP/bin/make_motd | 284 + clients/HP/bin/make_resolv_conf | 49 + clients/HP/bin/makehome | 160 + clients/HP/bin/makehosts | 44 + clients/HP/bin/mkpass | 238 + clients/HP/bin/mkpass-nisclient | 234 + clients/HP/bin/mkpass.prenis | 227 + clients/HP/bin/mkpty | 102 + clients/HP/bin/monitor_taskbroker | 39 + clients/HP/bin/mount_nfs | 43 + clients/HP/bin/mount_project_lvm | 31 + clients/HP/bin/mountlvm | 54 + clients/HP/bin/nisclient-11.x | 152 + clients/HP/bin/nisclient-9.x | 176 + clients/HP/bin/nisclient-adl | 166 + clients/HP/bin/pdl-config | 446 ++ clients/HP/bin/pdl-config.src | 159 + clients/HP/bin/pdl-new-passwd | 63 + clients/HP/bin/pingnet | 115 + clients/HP/bin/reinstall_patches_bundle | 61 + clients/HP/bin/reinstall_unixsysadm | 80 + clients/HP/bin/remount_viewserver | 48 + clients/HP/bin/restart_system | 45 + clients/HP/bin/restrict_passwd | 161 + clients/HP/bin/roll_logs | 115 + clients/HP/bin/root | 43 + clients/HP/bin/rootmail | 49 + clients/HP/bin/rs | 1 + clients/HP/bin/security | 157 + clients/HP/bin/setup | 75 + clients/HP/bin/setup.new.system2 | 195 + clients/HP/bin/setup_cron | 28 + clients/HP/bin/setup_ssmtp | 58 + clients/HP/bin/switch_licsrv | 48 + clients/HP/bin/switch_region | 48 + clients/HP/bin/switch_rgy | 48 + clients/HP/bin/sysinfo | 5851 +++++++++++++++++ clients/HP/bin/sysmodel | 230 + clients/HP/bin/tbdf | 76 + clients/HP/bin/testmach | 59 + clients/HP/bin/trim_sd_logs | 124 + clients/HP/bin/unmount_nfs | 93 + clients/HP/bin/update_machine_info | 110 + clients/HP/bin/veritas | 32 + clients/HP/bin/vicron | 50 + clients/HP/bin/viewservers | 39 + clients/HP/bin/vobadm | 37 + clients/HP/bin/vobservers | 39 + clients/HP/bin/whoison | 72 + clients/HP/bin/whosdown | 199 + clients/HP/bin/wrkservers | 40 + clients/HP/cygwin_setup | 241 + clients/HP/daily | 151 + clients/HP/packet2vob | 37 + clients/HP/pingnet | 115 + clients/HP/roll_logs | 115 + clients/HP/setup_cron | 28 + clients/HP/setup_ssmtp | 58 + clients/LynuxWorks/bin/ecrc | 177 + clients/LynuxWorks/bin/ecrd | 564 ++ clients/LynuxWorks/bin/ecrdesc | 65 + clients/LynuxWorks/bin/files4cr | 202 + clients/LynuxWorks/bin/files4ecr | 185 + clients/LynuxWorks/bin/files4tag | 17 + clients/LynuxWorks/lib/Diff.pm | 584 ++ clients/LynuxWorks/lib/ecrc.pm | 220 + clients/Salira/CheckinPostop.pl | 102 + clients/Salira/CheckinPreop.pl | 293 + clients/Salira/NotifyTrigger.pl | 137 + clients/Salira/RemoveEmptyBranch.pl | 104 + clients/Salira/SetOwnershipTrigger.pl | 29 + 161 files changed, 31780 insertions(+) create mode 100644 clients/Ameriquest/bin/PMO-CM.cmd create mode 100644 clients/Ameriquest/bin/backup.pl create mode 100644 clients/Ameriquest/bin/ccase_lock_vobs.bat create mode 100644 clients/Ameriquest/bin/ccase_unlock_vobs.bat create mode 100644 clients/Ameriquest/bin/ccverify.pl create mode 100644 clients/Ameriquest/bin/cmverify.cmd create mode 100644 clients/Ameriquest/bin/cqverify.pl create mode 100644 clients/Ameriquest/bin/getccgroups.vbs create mode 100644 clients/Ameriquest/bin/tagit.pl create mode 100644 clients/Ameriquest/bin/vobsize.pl create mode 100644 clients/Ameriquest/bin/whosin.cmd create mode 100644 clients/Ameriquest/bin/whosin.vbs create mode 100644 clients/Ameriquest/triggers/ASAPNotify.msg create mode 100644 clients/Ameriquest/triggers/ASAP_BUS_REQ_Notify.msg create mode 100644 clients/Ameriquest/triggers/AddExecute.pl create mode 100644 clients/Ameriquest/triggers/CommentSQLCode.pl create mode 100644 clients/Ameriquest/triggers/LogActivity.pl create mode 100644 clients/Ameriquest/triggers/NoPBLs.pl create mode 100644 clients/Ameriquest/triggers/Permissions.pl create mode 100644 clients/Ameriquest/triggers/defaria_notify.msg create mode 100644 clients/GD/FSMon/Filesystem.pm create mode 100644 clients/GD/FSMon/Fonts/GeosansLight.ttf create mode 100644 clients/GD/FSMon/Fonts/MankSans.ttf create mode 100644 clients/GD/FSMon/Fonts/Silkscreen.ttf create mode 100644 clients/GD/FSMon/Fonts/pf_arma_five.ttf create mode 100644 clients/GD/FSMon/Fonts/tahoma.ttf create mode 100644 clients/GD/FSMon/FsmonDB.php create mode 100644 clients/GD/FSMon/FsmonDB.pm create mode 100644 clients/GD/FSMon/FsmonDB.sql create mode 100644 clients/GD/FSMon/Fsutils.php create mode 100755 clients/GD/FSMon/fsmon create mode 100644 clients/GD/FSMon/fsmon.php create mode 100644 clients/GD/FSMon/graphFS.php create mode 100644 clients/GD/FSMon/pChart/pCache.class create mode 100644 clients/GD/FSMon/pChart/pChart.class create mode 100644 clients/GD/FSMon/pChart/pData.class create mode 100644 clients/GD/FSMon/testgraph.php create mode 100644 clients/GD/cqtool.tar.gz create mode 100644 clients/GD/cqtool/CreateHelpDeskUI.pm create mode 100644 clients/GD/cqtool/CreateWORUI.pm create mode 100755 clients/GD/cqtool/cqtool.pl create mode 100644 clients/GD/rexec create mode 100644 clients/HP/bin/SCS create mode 100644 clients/HP/bin/add_email create mode 100644 clients/HP/bin/add_moa create mode 100644 clients/HP/bin/add_postnote create mode 100644 clients/HP/bin/add_sharedx create mode 100644 clients/HP/bin/add_synchronize create mode 100644 clients/HP/bin/add_user create mode 100644 clients/HP/bin/adl-config create mode 100644 clients/HP/bin/adl-config.src create mode 100644 clients/HP/bin/allmach create mode 100644 clients/HP/bin/arcserverenv create mode 100644 clients/HP/bin/atria.scs create mode 100644 clients/HP/bin/bigfiles create mode 100644 clients/HP/bin/buildservers create mode 100644 clients/HP/bin/check_security create mode 100644 clients/HP/bin/cleantmps create mode 100644 clients/HP/bin/config_disk_array create mode 100644 clients/HP/bin/configure_machine create mode 100644 clients/HP/bin/cpucount create mode 100644 clients/HP/bin/cygwin_setup create mode 100644 clients/HP/bin/daily create mode 100644 clients/HP/bin/desktopmach create mode 100644 clients/HP/bin/diskspace create mode 100644 clients/HP/bin/display_path create mode 100644 clients/HP/bin/dum create mode 100644 clients/HP/bin/fix_automounts create mode 100644 clients/HP/bin/fix_startup create mode 100644 clients/HP/bin/fixlocalaliases create mode 100644 clients/HP/bin/fixntp create mode 100644 clients/HP/bin/fixrhosts create mode 100644 clients/HP/bin/fk1 create mode 100644 clients/HP/bin/fk2 create mode 100644 clients/HP/bin/fk3 create mode 100644 clients/HP/bin/fk4 create mode 100644 clients/HP/bin/fk5 create mode 100644 clients/HP/bin/fk6 create mode 100644 clients/HP/bin/get_info create mode 100644 clients/HP/bin/infrastructuremach create mode 100644 clients/HP/bin/insert create mode 100644 clients/HP/bin/install_new_kernel create mode 100644 clients/HP/bin/install_pwplus create mode 100644 clients/HP/bin/inventory create mode 100644 clients/HP/bin/lpsetup create mode 100644 clients/HP/bin/lspatches create mode 100644 clients/HP/bin/lsproduct create mode 100644 clients/HP/bin/lvs create mode 100644 clients/HP/bin/machine_info create mode 100644 clients/HP/bin/machine_stats create mode 100644 clients/HP/bin/make_motd create mode 100644 clients/HP/bin/make_resolv_conf create mode 100644 clients/HP/bin/makehome create mode 100644 clients/HP/bin/makehosts create mode 100644 clients/HP/bin/mkpass create mode 100644 clients/HP/bin/mkpass-nisclient create mode 100644 clients/HP/bin/mkpass.prenis create mode 100644 clients/HP/bin/mkpty create mode 100644 clients/HP/bin/monitor_taskbroker create mode 100644 clients/HP/bin/mount_nfs create mode 100644 clients/HP/bin/mount_project_lvm create mode 100644 clients/HP/bin/mountlvm create mode 100644 clients/HP/bin/nisclient-11.x create mode 100644 clients/HP/bin/nisclient-9.x create mode 100644 clients/HP/bin/nisclient-adl create mode 100644 clients/HP/bin/pdl-config create mode 100644 clients/HP/bin/pdl-config.src create mode 100644 clients/HP/bin/pdl-new-passwd create mode 100644 clients/HP/bin/pingnet create mode 100644 clients/HP/bin/reinstall_patches_bundle create mode 100644 clients/HP/bin/reinstall_unixsysadm create mode 100644 clients/HP/bin/remount_viewserver create mode 100644 clients/HP/bin/restart_system create mode 100644 clients/HP/bin/restrict_passwd create mode 100644 clients/HP/bin/roll_logs create mode 100644 clients/HP/bin/root create mode 100644 clients/HP/bin/rootmail create mode 100644 clients/HP/bin/rs create mode 100644 clients/HP/bin/security create mode 100644 clients/HP/bin/setup create mode 100644 clients/HP/bin/setup.new.system2 create mode 100644 clients/HP/bin/setup_cron create mode 100644 clients/HP/bin/setup_ssmtp create mode 100644 clients/HP/bin/switch_licsrv create mode 100644 clients/HP/bin/switch_region create mode 100644 clients/HP/bin/switch_rgy create mode 100644 clients/HP/bin/sysinfo create mode 100644 clients/HP/bin/sysmodel create mode 100644 clients/HP/bin/tbdf create mode 100644 clients/HP/bin/testmach create mode 100644 clients/HP/bin/trim_sd_logs create mode 100644 clients/HP/bin/unmount_nfs create mode 100644 clients/HP/bin/update_machine_info create mode 100644 clients/HP/bin/veritas create mode 100644 clients/HP/bin/vicron create mode 100644 clients/HP/bin/viewservers create mode 100644 clients/HP/bin/vobadm create mode 100644 clients/HP/bin/vobservers create mode 100644 clients/HP/bin/whoison create mode 100644 clients/HP/bin/whosdown create mode 100644 clients/HP/bin/wrkservers create mode 100755 clients/HP/cygwin_setup create mode 100755 clients/HP/daily create mode 100755 clients/HP/packet2vob create mode 100755 clients/HP/pingnet create mode 100755 clients/HP/roll_logs create mode 100755 clients/HP/setup_cron create mode 100755 clients/HP/setup_ssmtp create mode 100644 clients/LynuxWorks/bin/ecrc create mode 100644 clients/LynuxWorks/bin/ecrd create mode 100644 clients/LynuxWorks/bin/ecrdesc create mode 100644 clients/LynuxWorks/bin/files4cr create mode 100644 clients/LynuxWorks/bin/files4ecr create mode 100644 clients/LynuxWorks/bin/files4tag create mode 100644 clients/LynuxWorks/lib/Diff.pm create mode 100644 clients/LynuxWorks/lib/ecrc.pm create mode 100644 clients/Salira/CheckinPostop.pl create mode 100644 clients/Salira/CheckinPreop.pl create mode 100644 clients/Salira/NotifyTrigger.pl create mode 100644 clients/Salira/RemoveEmptyBranch.pl create mode 100644 clients/Salira/SetOwnershipTrigger.pl diff --git a/clients/Ameriquest/bin/PMO-CM.cmd b/clients/Ameriquest/bin/PMO-CM.cmd new file mode 100644 index 0000000..e106e07 --- /dev/null +++ b/clients/Ameriquest/bin/PMO-CM.cmd @@ -0,0 +1,13 @@ +@echo off +rem ------------------------------------------------------------------------- +rem - +rem - File: PMO-CM.cmd +rem - Description: This script is designed to be called when the user +rem - logs into his/her desktop. The purpose of this +rem - script is to do start up type things. Currently this +rem - means nothing but in the future we may add things. +rem - Author: Andrew@DeFaria.com +rem - Created: Mon Mar 27 14:00:00 PST 2004 +rem - Language: Dumb CMD stuff! :-( +rem - +rem ------------------------------------------------------------------------- diff --git a/clients/Ameriquest/bin/backup.pl b/clients/Ameriquest/bin/backup.pl new file mode 100644 index 0000000..0f98e98 --- /dev/null +++ b/clients/Ameriquest/bin/backup.pl @@ -0,0 +1,251 @@ +#!/usr/bin/perl +################################################################################# +# +# File: backup.pl +# Description: This script performs backups of vobs. By backup we mean that it +# will lock a vob then copy that vobs storage area to another area +# on disk then unlock the vob. +# Author: Andrew@DeFaria.com +# Created: June 23, 2004 +# Language: Perl +# Warnings: Since we use Windows commands like xcopy this script will only +# work under Windows. +# +# (c) Copyright 2004, Andrew@DeFaria.com, all rights reserved. +# +################################################################################ +use strict; +use warnings; + +my $me = $0; + +$me =~ s/\.\///; + +my $backup_loc = "d:\\backup"; +my $history_loc = "d:\\vobstore\\backup"; +my $from_loc = "d:\\"; +my $vob_server = "rtnlprod01"; +my $day_nbr = (localtime ()) [6]; # Day # of week. +my $total_size; + +# Options +my $verbose = "no"; + +sub Duration { + use integer; + + my $start_time = shift; + my $end_time = shift; + + my $hours; + my $minutes; + my $seconds = $end_time - $start_time; + + if ($seconds eq 0) { + return "less than a second"; + } elsif ($seconds eq 1) { + return "a second"; + } elsif ($seconds < 60) { + return "$seconds seconds"; + } elsif ($seconds < (60 * 60)) { + $minutes = $seconds / 60; + $seconds = $seconds % 60; + my $minutes_string = ($minutes eq 1) ? "minute" : "minutes"; + my $seconds_string = ($seconds eq 1) ? "second" : "seconds"; + return "$minutes $minutes_string $seconds $seconds_string"; + } else { + $hours = $seconds / (60 * 60); + $seconds = $seconds % (60 * 60); + $minutes = $seconds / 60; + $seconds = $seconds % 60; + my $hours_string = ($hours eq 1) ? "hour" : "hours"; + my $minutes_string = ($minutes eq 1) ? "minute" : "minutes"; + my $seconds_string = ($seconds eq 1) ? "second" : "seconds"; + return "$hours $hours_string $minutes $minutes_string $seconds $seconds_string"; + } # fi +} # Duration + +sub Usage { + print "Usage $me: [-v]"; + print "\nWhere:\n"; + print "\t-v\tVerbose\n"; + exit 1; +} # Usage + +sub verbose { + my $msg = shift; + + print "$msg\n" if $verbose eq "yes"; +} # verbose + +sub warning { + my $msg = shift; + + print "$me: WARNING: $msg\n"; +} # warning + +sub error { + my $msg = shift; + my $errno = shift; + + print "$me: ERROR "; + print "# $errno: " if defined $errno; + print "$msg\n"; + + exit $errno if defined $errno; +} # error + +sub VobSize { + my $vob = shift; + + my $size = 0; + my $cleartext = 0; + my @space = `cleartool space $vob 2> NUL`; + + foreach (@space) { + if (/Subtotal $/) { + ($size) = split; + } # if + if (/cleartext/) { + ($cleartext) = split; + } # if + } # foreach + + return $size - $cleartext; +} # VobSize + +sub LockVobs { + my @vobs = @_; + + my $status = 0; + + foreach (@vobs) { + chomp; + my $return_code = system "cleartool lock vob:$_ > NUL 2>&1"; + warning "Unable to lock vob $_" if $return_code ne 0; + $status += $return_code; + } # foreach + + return $status; +} # LockVobs + +sub UnlockVobs { + my @vobs = @_; + + my $status = 0; + + foreach (@vobs) { + my $return_code = system "cleartool unlock vob:$_ > NUL 2>&1"; + warning "Unable to unlock vob $_" if $return_code ne 0; + $status += $return_code; + } # foreach + + return $status; +} # UnlockVobs + +sub MoveOldStorage { + my $vob_storage_basename = shift; + + my $storage = "$backup_loc\\$vob_storage_basename"; + my $old_storage_loc = "$history_loc\\$day_nbr\\$vob_storage_basename"; + my @output; + + if (-e $old_storage_loc) { + @output = `rmdir /s /q $old_storage_loc`; + + if ($? ne 0) { + error "Error in removing old storage area $old_storage_loc"; + error (join "\n", @output); + } else { + verbose "Removed old storage area $old_storage_loc"; + } # if + } # if + + @output = `move $storage $old_storage_loc`; + + if ($? ne 0) { + error "Error in moving storage area $storage to $old_storage_loc"; + error (join "\n", @output); + } else { + verbose "Moved old storage $storage to $old_storage_loc"; + } # if +} # MoveOldStorage + +sub CopyStorage { + my $vob = shift; + my $from = shift; + my $vob_storage_basename = shift; + + my $to = "$backup_loc\\$vob_storage_basename"; + my $size = VobSize $vob; + + $total_size += $size; + verbose "Copying $vob ($size meg) from $from -> $to"; + + my $start_time = time; + + MoveOldStorage $vob_storage_basename if -e $to; + + # Copy storage but exclude any file containing strings found in the + # d:\backup\exclude.strings file See + # http://www-1.ibm.com/support/docview.wss?rs=0&q1=being-deleted&uid=swg21129318&loc=en_US&cs=utf-8&cc=us&lang=en + # for more info. + my @output = `xcopy $from $to /q /e /i /h /k /x /exclude:d:\\backup\\exclude.strings`; + + my $end_time = time; + + if ($? ne 0) { + print "Error in copy of vob $vob\n"; + print @output; + } else { + verbose "Copying of $vob ($size meg) took " . Duration $start_time, $end_time; + } # if +} # CopyStorage + +# Get parms +while ($#ARGV >= 0) { + if ($ARGV [0] eq "-v") { + $verbose = "yes"; + shift; + next; + } # if + + if ($ARGV [0] ne "") { + error "Unknown option: \"" . $ARGV [0] . "\"\n"; + Usage; + } # if +} # while + +# Iterrate through the list of vobs +my $start_time = time; + +my @vobs = `cleartool lsvob -short -host $vob_server 2> NUL`; + +warning "Unable to lock all vobs" if (LockVobs @vobs) ne 0; + +foreach (@vobs) { + chomp; + my $line = `cleartool lsvob $_ 2> NUL`; + chomp $line; + $line =~ s/\//\\/g; + + my $storage; + + if ($line =~ m/(\\\\\S*)/) { + $storage = $1; + } # if + + $storage =~ s/\\\\$vob_server\\/$from_loc/; + + my $vob_storage_basename = substr ($storage, rindex ($storage, "\\") + 1); + + CopyStorage $_, $storage, $vob_storage_basename; +} # foreach + +warning "Unable to unlock all vobs" if (UnlockVobs @vobs) ne 0; + +my $end_time = time; + +verbose "Total of $total_size meg copied in: " . Duration $start_time, $end_time; +# All done... +exit 0; diff --git a/clients/Ameriquest/bin/ccase_lock_vobs.bat b/clients/Ameriquest/bin/ccase_lock_vobs.bat new file mode 100644 index 0000000..d413e8c --- /dev/null +++ b/clients/Ameriquest/bin/ccase_lock_vobs.bat @@ -0,0 +1,9 @@ +@echo off + +set view_server=rtnlprod02 +set view_share=viewstore +set view=PMO +set vob=CM_TOOLS +set bin_path=\\%view_server%\%view_share%\%view%\%vob%\bin + +"%CLEARCASEHOME%\bin\ccperl.exe" "%bin_path%\lockvobs.pl" diff --git a/clients/Ameriquest/bin/ccase_unlock_vobs.bat b/clients/Ameriquest/bin/ccase_unlock_vobs.bat new file mode 100644 index 0000000..f140ed5 --- /dev/null +++ b/clients/Ameriquest/bin/ccase_unlock_vobs.bat @@ -0,0 +1,9 @@ +@echo off + +set view_server=rtnlprod02 +set view_share=viewstore +set view=PMO +set vob=CM_TOOLS +set bin_path=\\%view_server%\%view_share%\%view%\%vob%\bin + +"%CLEARCASEHOME%\bin\ccperl.exe" "%bin_path%\lockvobs.pl" -u diff --git a/clients/Ameriquest/bin/ccverify.pl b/clients/Ameriquest/bin/ccverify.pl new file mode 100644 index 0000000..52b39fa --- /dev/null +++ b/clients/Ameriquest/bin/ccverify.pl @@ -0,0 +1,152 @@ +#!/usr/bin/perl -w +################################################################################# +# +# File: ccverify.pl +# Description: Verify that Rational Clearcase was installed correctly +# Author: Andrew@DeFaria.com +# Created: Mon Mar 15 08:48:24 PST 2004 +# Language: None +# +# (c) Copyright 2004, Andrew@DeFaria.com, all rights reserved. +# +################################################################################ +use strict; + +my $ccverify = "1.0"; +my $logpath = "\\\\rtnlprod02\\viewstore\\PMO\\CM_TOOLS\\log"; +my $hostname = `hostname`; chomp $hostname; +my $logfile = "$logpath\\$hostname.log"; +my $status = 0; +my $tag = "ccverify"; + +open LOGFILE, ">>$logfile" + or die "Unable to open logfile: $logfile - $!\n"; + +sub logmsg { + my $message = shift; + + print "$message\n"; + print LOGFILE "$message\n"; +} # logmsg + +sub mktag { + my $tag = shift; + + my $status = system "cleartool lsvob \\$tag > NUL 2>&1"; + + if ($status ne 0) { + return system "cleartool mktag -vob -tag \\$tag \\\\rtnlprod01\\vobstore\\$tag.vbs > NUL 2>&1"; + } # if +} # mktag + +sub rmtag { + my $tag = shift; + + return system "cleartool rmtag -vob \\$tag > NUL 2>&1"; +} # rmtag + +sub rmview { + my $tag = shift; + + return system "cleartool rmview -force -tag $tag > NUL 2>&1"; +} # rmview + +sub mkview { + my $tag = shift; + + my $status = system "cleartool lsview -short $tag > NUL 2>&1"; + + if ($status ne 0) { + return system "cleartool mkview -tag $tag -stgloc -auto > NUL 2>&1"; + } else { + rmview $tag; + return system "cleartool mkview -tag $tag -stgloc -auto > NUL 2>&1"; + } # if +} # mkview + +sub mount_vob { + my $tag = shift; + + mktag $tag; + + return system "cleartool mount \\$tag > NUL 2>&1"; +} # mount_vob + +sub umount_vob { + my $tag = shift; + + my $status = system "cleartool umount \\$tag > NUL 2>&1"; + + rmtag $tag; + + return $status; +} # umount_vob + +my $version = `cleartool -ver`; +my $primary_group = $ENV {CLEARCASE_PRIMARY_GROUP}; + +my @hostinfo = `cleartool hostinfo -long`; +my $region = "Not Set"; + +foreach (@hostinfo) { + chomp; + if (/\s*Registry region:\s*(\S*)/) { + $region = $1; + last; + } # if +} # foreach + +logmsg "CCVerify Version $ccverify"; +logmsg "Verifying Clearcase installation on $hostname (" . scalar (localtime) . ")\n"; +logmsg "Clearcase Version Information\n"; +logmsg "$version\n"; + +if (!defined $primary_group) { + $primary_group = ""; + $status++; +} # if + +logmsg "Clearcase Primary Group:\t$primary_group"; +logmsg "Clearcase Region:\t\t$region\n"; + +if (mkview ($tag) eq 0) { + logmsg "Created a dynamic view named $tag"; +} else { + $status++; + logmsg "Unable to create the $tag dynamic view!"; +} # if + +if (mount_vob ($tag) eq 0) { + logmsg "Mounted the vob \\$tag"; +} else { + $status++; + logmsg "Unable to mount the vob \\$tag"; +} # if + +if (umount_vob ($tag) eq 0) { + logmsg "Unmounted the vob \\$tag"; +} else { + $status++; + logmsg "Unable to unmount vob \\$tag"; +} # if + +if (rmview ($tag) eq 0) { + logmsg "Removed view $tag"; +} else { + $status++; + logmsg "Unable to remove view $tag"; +} # if + +if ($status eq 0) { + logmsg +"\n-------------------------------------------- +Clearcase installed and functioning properly +--------------------------------------------\n"; +} else { + logmsg +"\n------------------------------------------------ +Clearcase NOT installed and functioning properly +------------------------------------------------\n"; +} # if + +exit $status; diff --git a/clients/Ameriquest/bin/cmverify.cmd b/clients/Ameriquest/bin/cmverify.cmd new file mode 100644 index 0000000..12516f7 --- /dev/null +++ b/clients/Ameriquest/bin/cmverify.cmd @@ -0,0 +1,53 @@ +@echo off +rem ------------------------------------------------------------------------- +rem - +rem - File: verify.cmd +rem - Description: This command verifies the installation of Rational +rem - tools. +rem - Author: Andrew@DeFaria.com +rem - Created: Mon Mar 28 14:52:00 PST 2004 +rem - Language: Dumb CMD stuff! :-( +rem - Parameters: There is only one parameter that is taken and that is +rem - the number of seconds that cqverify should wait +rem - before starting. This is only needed if this cmvery +rem - command is run from the various install scripts. +rem - +rem ------------------------------------------------------------------------- +set ccperl=C:\Program Files\Rational\Clearcase\bin\ccperl.exe +set cqperl=C:\Program Files\Rational\Common\cqperl.exe +set ccverify=\\rtnlprod02\viewstore\PMO\CM_TOOLS\bin\ccverify.pl +set cqverify=\\rtnlprod02\viewstore\PMO\CM_TOOLS\bin\cqverify.pl +set wait=\\rtnlprod02\viewstore\PMO\CM_TOOLS\bin\wait.pl +set logfile=\\rtnlprod02\viewstore\PMO\CM_TOOLS\log\%COMPUTERNAME%.log + +rem Clear out logfile +if exist %logfile% del /q /f %logfile% + +rem Check for necessary tools +set msg= +if not exist "%ccperl%" set msg=Clearcase is not installed +if not exist "%ccverify%" set msg=Unable to find ccverify script (%ccverify%)! + +if not "%msg%" == "" goto Error + +rem Since we found ccperl assume that the installation was done so now +rem we'll check the configuration +"%ccperl%" %ccverify% + +rem Wait for Clearquest/TUP install to finish +if not "%1" == "" echo Waitng for Clearquest/TUP installation to finish&& "%ccperl%" %wait% %1 + +rem Now check cq +if not exist "%cqperl%" set msg=Clearquest/TUP is not installed +if not exist "%cqverify%" set msg=Unable to find cqverify script (%cqverify%)! + +if not "%msg%" == "" goto Error + +"%cqperl%" %cqverify% +goto EXIT + +:Error +echo %msg% +echo %msg% >> %logfile% + +:EXIT \ No newline at end of file diff --git a/clients/Ameriquest/bin/cqverify.pl b/clients/Ameriquest/bin/cqverify.pl new file mode 100644 index 0000000..6bb97f0 --- /dev/null +++ b/clients/Ameriquest/bin/cqverify.pl @@ -0,0 +1,74 @@ +#!/usr/bin/perl -w +################################################################################# +# +# File: cqverify.pl +# Description: Verify that Rational Clearquest was installed correctly +# Author: Andrew@DeFaria.com +# Created: Mon Mar 15 08:48:24 PST 2004 +# Language: Perl +# +################################################################################ +use strict; +use CQPerlExt; + +my $cqverify = "1.0"; +my $logpath = "\\\\rtnlprod02\\viewstore\\PMO\\CM_TOOLS\\log"; +my $hostname = `hostname`; chomp $hostname; +my $logfile = "$logpath\\$hostname.log"; +my $status = 0; + +open LOGFILE, ">>$logfile" + or die "Unable to open logfile: $logfile - $!\n"; + +sub logmsg { + my $message = shift; + + print "$message\n"; + print LOGFILE "$message\n"; +} # logmsg + +# Log in to CQ as guest +logmsg "CQVerify Version $cqverify"; +logmsg "Verifying Clearquest/TUP installation on $hostname (" . scalar (localtime) . ")"; + +my $CQsession = CQPerlExt::CQSession_Build () + or logmsg "Unable to establish CQSession", die; + +my ($queryDef, $resultSet, $result); +eval { + $CQsession->UserLogon ("guest", "guest", "AMQST", "AMQST"); + # Construct a CQ query that will return the ID of the first CQ record + $queryDef = $CQsession->BuildQuery ("defect"); + $queryDef->BuildField ("id"); + $resultSet = $CQsession->BuildResultSet ($queryDef); + $resultSet->Execute; + $resultSet->GetNumberOfColumns; + $status = $resultSet->MoveNext; + $result = $resultSet->GetColumnValue ("1"); + CQSession::Unbuild ($CQsession); +}; + +if ($@) { + logmsg $@; + logmsg +"----------------------------------------------------- +Clearquest/TUP NOT installed and functioning properly +-----------------------------------------------------"; + exit 1; +} else { + if ($result =~ m/^AMQST/) { + logmsg "Clearquest query succeeded"; + logmsg +"------------------------------------------------- +Clearquest/TUP installed and functioning properly +-------------------------------------------------"; + exit 0; + } else { + logmsg "Value returned not was not expected: $result"; + logmsg +"----------------------------------------------------- +Clearquest/TUP NOT installed and functioning properly +-----------------------------------------------------"; + exit 1; + } # if +} # if diff --git a/clients/Ameriquest/bin/getccgroups.vbs b/clients/Ameriquest/bin/getccgroups.vbs new file mode 100644 index 0000000..46da523 --- /dev/null +++ b/clients/Ameriquest/bin/getccgroups.vbs @@ -0,0 +1,215 @@ +On Error Resume Next + +Set WshShell = WScript.CreateObject ("WScript.Shell") +Set fso = CreateObject ("Scripting.FileSystemObject") + +groups_file = "\\rtnlprod02\viewstore\PMO\CM_TOOLS\etc\groups.dat" + +' Simple routine to shorten WScript.Echo! +Sub echo (msg) + WScript.Echo msg +End Sub ' echo + +Sub Email_Owner (sendTo, groupname, members) + sch = "http://schemas.microsoft.com/cdo/configuration/" + + Set cdoConfig = CreateObject ("CDO.Configuration") + + With cdoConfig.Fields + .Item (sch & "sendusing") = 2 ' cdoSendUsingPort + .Item (sch & "smtpserver") = "appsmtp.ameriquest.net" + .Update + End With + + Set email_msg = CreateObject ("CDO.Message") + + email_msg.Configuration = cdoConfig + email_msg.From = "PMO-CM@Ameriquest.net" + email_msg.To = sendTo + email_msg.Subject = "Members of the " & UCase (groupname) & " Group" + email_msg.HTMLBody = "

Members of the " & UCase (groupname) & " Group

 

    " + + previous_member = "" + For Each member in members + If member <> previous_member Then + email_msg.HTMLBody = email_msg.HTMLBody & "
  1. " & member & "
  2. " + previous_member = member + End If + Next + + email_msg.HTMLBody = email_msg.HTMLBody & "
" + email_msg.Send +End Sub + +' Routine to push things onto an array +Sub pushArray (a, e) + On Error Resume Next + size = UBound (a) + + If Err.Number <> 0 Then + size = 0 + Else + size = size + 1 + End If + + ReDim preserve a (size) + + a (UBound (a)) = e +End Sub 'pushArray + +' The famous QuickSort! +Sub QuickSort (vec, loBound, hiBound) + Dim pivot + Dim loSwap + Dim hiSwap + Dim temp + + ' This procedure is adapted from the algorithm given in: + ' Data Abstractions & Structures using C++ by + ' Mark Headington and David Riley, pg. 586 + ' Quicksort is the fastest array sorting routine for + ' unordered arrays. Its big O is n log n + + ' Two items to sort + If hiBound - loBound = 1 Then + If vec(loBound) > vec(hiBound) Then + temp = vec (loBound) + vec (loBound) = vec (hiBound) + vec (hiBound) = temp + End If + End If + + ' Three or more items to sort + pivot = vec (Int ((loBound + hiBound) / 2)) + vec (Int ((loBound + hiBound) / 2)) = vec (loBound) + vec (loBound) = pivot + loSwap = loBound + 1 + hiSwap = hiBound + + Do + ' Find the right loSwap + While loSwap < hiSwap and vec (loSwap) <= pivot + loSwap = loSwap + 1 + Wend + + ' Find the right hiSwap + While vec (hiSwap) > pivot + hiSwap = hiSwap - 1 + Wend + + ' Swap values if loSwap is less then hiSwap + If loSwap < hiSwap Then + temp = vec (loSwap) + vec (loSwap) = vec (hiSwap) + vec (hiSwap) = temp + End If + Loop While loSwap < hiSwap + + vec (loBound) = vec(hiSwap) + vec (hiSwap) = pivot + + ' Recursively call function .. the beauty of Quicksort + ' 2 or more items in first section + If loBound < (hiSwap - 1) Then + QuickSort vec, loBound, hiSwap - 1 + End If + + ' 2 or more items in second section + If hiSwap + 1 < hibound Then + QuickSort vec, hiSwap+1, hiBound + End If +End Sub ' QuickSort + +' Get the group members out of Active Directory and push them +' onto the grp_mbrs array +Sub DumpGroup (groupname) + ' Create an LDAP object and set up the search for groupname + On Error Resume Next + Err.Clear + Set LDAPGroups = GetObject ( _ + "LDAP://cn=" & _ + groupname & _ + ",ou=apps,ou=Groups,ou=Corp,dc=ameriquest,dc=net" _ + ) + + If Err.Number <> 0 Then + Err.Clear + echo "Warning: " & UCase (groupname) & " is empty!" + Exit Sub + End If + + LDAPGroups.GetInfo + + ' Get an array of members + members = LDAPGroups.GetEx ("member") + + ' For each member get their displayName and push it onto the array + For Each member in members + Set user = GetObject ("LDAP://"& member) + LDAPGroups.filter = array ("user") + user.GetInfo + + ' Kludgy way to check to see if this is a group: We attempt + ' to get groupType which should only be in a group type + ' record. If this fails then process the member as an + ' individual member, otherwise recurse to process the + ' group within the group... + Err.Clear + user.Get ("groupType") + + if Err.Number = 0 Then + Err.Clear + groupname = LCase (user.Get ("cn")) + DumpGroup (groupname) + Else + name = user.Get ("displayName") + pushArray grp_mbrs, name + End If + Next +End Sub + +' Open the groups definition file +Set groups = fso.OpenTextFile (groups_file) + +Do While Not groups.AtEndOfStream + line = groups.ReadLine + + ' *** Need to also skip blank lines + If Line <> "" Then + If InStr (line, "#") <> 1 Then + With New RegExp + .Pattern = "\s+" + .Global = True + line = Trim (.replace (line, " ")) + End With + + ' Split out fields + groupname = "" + owner = "" + fields = Split (line) + groupname = LCase (fields (0)) + owner = LCase (fields (1)) + + echo "Processing group " & UCase (groupname) & " (" & owner & ")" + + Dim grp_mbrs () + + ' Get members of this group + DumpGroup (groupname) + + If UBound (grp_mbrs) > 0 Then + ' Sort them + QuickSort grp_mbrs, LBound (grp_mbrs), UBound (grp_mbrs) + + ' Output them + If owner <> "" Then + Email_Owner owner, groupname, grp_mbrs + Else + echo "Group: " & UCase (groupname) & " has no owner email in " & groups_file + End If + + Erase grp_mbrs + End If + End If + End If +Loop diff --git a/clients/Ameriquest/bin/tagit.pl b/clients/Ameriquest/bin/tagit.pl new file mode 100644 index 0000000..9696b94 --- /dev/null +++ b/clients/Ameriquest/bin/tagit.pl @@ -0,0 +1,183 @@ +#!/usr/bin/perl -w +################################################################################# +# +# File: tagit +# Description: Script to tag views or vobs into the current region. The main +# motivation for this script is to be able to tag things quickly +# and easily. As such we employ heuristics to find these objects. +# Author: Andrew@DeFaria.com +# Created: Fri Apr 9 12:19:04 PDT 2004 +# Language: Perl +# +# (c) Copyright 2004, Andrew@DeFaria.com, all rights reserved. +# +################################################################################ +use strict; +use File::Spec; + +my $me; + +BEGIN { + # Extract relative path and basename from script name. + $0 =~ /.*[\/\\](.*)/; + $me = (!defined $1) ? $0 : $1; +} # BEGIN + +# Check to see if we are running on Windows +my $windows = ($^O =~ /MSWin/) ? "yes" : "no"; +my $null = $windows eq "yes" ? "NUL" : "/dev/null"; +my $backslashes = $windows eq "yes" ? "\\" : "\\\\"; + +sub Usage { + print "Usage $me: [[ -view ] ] [ -vob ]\n"; + print "\nWhere:\n"; + print "\t-view\tView tag to tag in current region\n"; + print "\t-vob\tVob tag to tag in current region\n"; + exit 1; +} # Usage + +sub GetCurrentRegion { + my @lines = `cleartool hostinfo -l`; + + foreach (@lines) { + chomp; chop $_ if /\r/; + + if (/Registry region: (\S+)/) { + return $1; + } # if + } # foreach + + return undef; +} # GetCurrentRegion + +my $current_region = GetCurrentRegion; +my @regions = `cleartool lsregion`; + +sub FindView { + my $view_tag = shift; + + foreach (@regions) { + chomp; chop if /\r/; + + my $output = `cleartool lsview -region $_ $view_tag 2> $null`; + chomp $output; chop $output if /\r/; + + if ($output =~ /$view_tag\s*(.*)/) { + my $view_storage = $1; + $view_storage =~ tr /\\/\/\//; + return ($view_storage, $_); + } # if + } # foreach + + return; +} # FindView + +sub FindVob { + my $vob_tag = shift; + + foreach (@regions) { + chomp; chop if /\r/; + + my $output = `cleartool lsvob -region $_ $backslashes$vob_tag 2> $null`; + chomp $output; chop $output if /\r/; + + if ($output =~ /$vob_tag\s*(\S*)/) { + my $vob_storage = $1; + $vob_storage =~ tr /\\/\/\//; + return ($vob_storage, $_); + } # if + } # foreach + + return; +} # FindVob + +sub MkViewTag { + my $view_tag = shift; + my $view_storage = shift; + + # Check to see if view tag already exists + if (!system "cleartool lsview $view_tag > $null 2>&1") { + print "$view_tag already exists in $current_region\n"; + return 0; + } else { + if (system "cleartool mktag -view -tag $view_tag $view_storage") { + die "Unable to make view tag: $view_tag in $current_region\n"; + } # if + return 1; + } # if +} # MkViewTag + +sub MkVobTag { + my $vob_tag = shift; + my $vob_storage = shift; + + # Check to see if vob tag already exists + if (!system "cleartool lsvob $backslashes$vob_tag > $null 2>&1") { + print "$vob_tag already exists in $current_region\n"; + return 1; + } else { + print "cleartool mktag -vob -tag $backslashes$vob_tag $vob_storage\n"; + if (system "cleartool mktag -vob -tag $backslashes$vob_tag $vob_storage") { + die "Unable to make vob tag: $vob_tag in $current_region\n"; + } # if + return 0; + } # if +} # MkVobTag + +my $view_tag; +my $vob_tag; + +# Get parms +while ($#ARGV >= 0) { + if ($ARGV [0] eq "-u" or $ARGV [0] eq "-usage") { + Usage; + } # if + + if ($ARGV [0] eq "-vob") { + shift; + $vob_tag = $ARGV [0]; + # Script all backslashes - we'll re-add them when needed... + $vob_tag =~ s/\\//; + shift; + next; + } # if + + if ($ARGV [0] eq "-view") { + shift; + $view_tag = $ARGV [0]; + next; + } # if +} # while + +if (!(defined $view_tag or defined $vob_tag)) { + Usage "Must specify a view or a vob to tag!"; +} # if + +if (defined $view_tag) { + my ($view_storage, $view_region) = FindView $view_tag; + + die "Unable to find view $view_tag in any region!\n" if !defined $view_region; + die "Unable to find storage area for $view_tag\n" if !defined $view_storage; + + if ($view_region eq $current_region) { + print "$view_tag already exists in $current_region\n"; + } else { + print "$view_tag, from $view_region region, added to $current_region region\n" if MkViewTag $view_tag, $view_storage;; + } # if +} # if + +if (defined $vob_tag) { + my ($vob_storage, $vob_region) = FindVob $vob_tag; + + die "Unable to find vob $vob_tag in any region!\n" if !defined $vob_region; + die "Unable to find storage area for $vob_tag\n" if !defined $vob_storage; + + if ($vob_region eq $current_region) { + print "$vob_tag already exists in $current_region\n"; + } else { + print "$vob_tag, from $vob_region, added to $current_region region\n" if MkVobTag $vob_tag, $vob_storage;; + } # if +} # if + +exit 0; + diff --git a/clients/Ameriquest/bin/vobsize.pl b/clients/Ameriquest/bin/vobsize.pl new file mode 100644 index 0000000..8574565 --- /dev/null +++ b/clients/Ameriquest/bin/vobsize.pl @@ -0,0 +1,72 @@ +#!/usr/bin/perl +use strict; +use warnings; + +my $windows = $^O =~ /MSWin/ ? "yes" : "no"; +my $vob_server = "rtnlprod01"; + +sub VobSize { + my $vob = shift; + + my @space; + + if ($windows eq "yes") { + @space = `cleartool space $vob 2>&1`; + } else { + @space = `cleartool space \\$vob 2>&1`; + } # if + + foreach (@space) { + chomp; chop if /\r/; + if (/Subtotal $/) { + my ($size) = split; + return $size; + } # if + } # foreach + + return 0; +} # VobSize + +my ( + $vob, + $size, + $count, + $total_size +); + +format STDOUT_TOP = + Nbr VOB Size +---- ----------------------- ----------- +. +format STDOUT = +@>>) @<<<<<<<<<<<<<<<<<<<<<< @>>>>>> Meg +$count,$vob,$size +. + +format TOTAL_TOP = +---- ----------------------- ----------- +. + +format TOTAL_LINE = +Total vob size: @>>>>>> Meg +$total_size +. + +my @vobs = `cleartool lsvob -short -host $vob_server`; + +foreach $vob (sort (@vobs)) { + $count++; + chomp $vob; chop $vob if $vob =~ /\r/; + + $size = VobSize $vob; + + $total_size += $size; + + write; $- = 1; +} # foreach + +$~ = "TOTAL_TOP"; +write; $- = 1; + +$~ = "TOTAL_LINE"; +write; $- = 1; diff --git a/clients/Ameriquest/bin/whosin.cmd b/clients/Ameriquest/bin/whosin.cmd new file mode 100644 index 0000000..c15be66 --- /dev/null +++ b/clients/Ameriquest/bin/whosin.cmd @@ -0,0 +1,3 @@ +@echo off +set CM_TOOLS=\\rtnlprod02\viewstore\PMO\CM_TOOLS +cscript /nologo "%CM_TOOLS%\bin\whosin.vbs" %* \ No newline at end of file diff --git a/clients/Ameriquest/bin/whosin.vbs b/clients/Ameriquest/bin/whosin.vbs new file mode 100644 index 0000000..5f836f6 --- /dev/null +++ b/clients/Ameriquest/bin/whosin.vbs @@ -0,0 +1,169 @@ +On Error Resume Next + +Set WshShell = WScript.CreateObject ("WScript.Shell") +Set fso = CreateObject ("Scripting.FileSystemObject") + +' Argument vector +Set ARGV = Wscript.Arguments + +' Simple routine to shorten WScript.Echo! +Sub echo (msg) + WScript.Echo msg +End Sub ' echo + +Sub Display_Members (sendTo, groupname, members) + echo "Members of the " & UCase (groupname) & " Group" + + previous_member = "" + For Each member in members + If member <> previous_member Then + echo vbTAB & member + previous_member = member + End If + Next +End Sub + +' Routine to push things onto an array +Sub pushArray (a, e) + On Error Resume Next + size = UBound (a) + + If Err.Number <> 0 Then + size = 0 + Else + size = size + 1 + End If + + ReDim preserve a (size) + + a (UBound (a)) = e +End Sub 'pushArray + +' The famous QuickSort! +Sub QuickSort (vec, loBound, hiBound) + Dim pivot + Dim loSwap + Dim hiSwap + Dim temp + + ' This procedure is adapted from the algorithm given in: + ' Data Abstractions & Structures using C++ by + ' Mark Headington and David Riley, pg. 586 + ' Quicksort is the fastest array sorting routine for + ' unordered arrays. Its big O is n log n + + ' Two items to sort + If hiBound - loBound = 1 Then + If vec(loBound) > vec(hiBound) Then + temp = vec (loBound) + vec (loBound) = vec (hiBound) + vec (hiBound) = temp + End If + End If + + ' Three or more items to sort + pivot = vec (Int ((loBound + hiBound) / 2)) + vec (Int ((loBound + hiBound) / 2)) = vec (loBound) + vec (loBound) = pivot + loSwap = loBound + 1 + hiSwap = hiBound + + Do + ' Find the right loSwap + While loSwap < hiSwap and vec (loSwap) <= pivot + loSwap = loSwap + 1 + Wend + + ' Find the right hiSwap + While vec (hiSwap) > pivot + hiSwap = hiSwap - 1 + Wend + + ' Swap values if loSwap is less then hiSwap + If loSwap < hiSwap Then + temp = vec (loSwap) + vec (loSwap) = vec (hiSwap) + vec (hiSwap) = temp + End If + Loop While loSwap < hiSwap + + vec (loBound) = vec(hiSwap) + vec (hiSwap) = pivot + + ' Recursively call function .. the beauty of Quicksort + ' 2 or more items in first section + If loBound < (hiSwap - 1) Then + QuickSort vec, loBound, hiSwap - 1 + End If + + ' 2 or more items in second section + If hiSwap + 1 < hibound Then + QuickSort vec, hiSwap+1, hiBound + End If +End Sub ' QuickSort + +' Get the group members out of Active Directory and push them +' onto the grp_mbrs array +Sub DumpGroup (groupname) + ' Create an LDAP object and set up the search for groupname + On Error Resume Next + Err.Clear + Set LDAPGroups = GetObject ( _ + "LDAP://cn=" & _ + groupname & _ + ",ou=apps,ou=Groups,ou=Corp,dc=ameriquest,dc=net" _ + ) + + If Err.Number <> 0 Then + Err.Clear + echo "Warning: " & UCase (groupname) & " is empty!" + Exit Sub + End If + + LDAPGroups.GetInfo + + ' Get an array of members + members = LDAPGroups.GetEx ("member") + + ' For each member get their displayName and push it onto the array + For Each member in members + Set user = GetObject ("LDAP://"& member) + LDAPGroups.filter = array ("user") + user.GetInfo + + ' Kludgy way to check to see if this is a group: We attempt + ' to get groupType which should only be in a group type + ' record. If this fails then process the member as an + ' individual member, otherwise recurse to process the + ' group within the group... + Err.Clear + user.Get ("groupType") + + if Err.Number = 0 Then + Err.Clear + groupname = LCase (user.Get ("cn")) + DumpGroup (groupname) + Else + name = user.Get ("displayName") + pushArray grp_mbrs, name + End If + Next +End Sub + +For Each ARG in ARGV + Dim grp_mbrs () + + ' Get members of this group + DumpGroup (ARG) + + If UBound (grp_mbrs) > 0 Then + ' Sort them + QuickSort grp_mbrs, LBound (grp_mbrs), UBound (grp_mbrs) + + ' Output them + Display_Members owner, ARG, grp_mbrs + + Erase grp_mbrs + echo + End If +Next \ No newline at end of file diff --git a/clients/Ameriquest/triggers/ASAPNotify.msg b/clients/Ameriquest/triggers/ASAPNotify.msg new file mode 100644 index 0000000..882ebb4 --- /dev/null +++ b/clients/Ameriquest/triggers/ASAPNotify.msg @@ -0,0 +1,16 @@ +From: ASAP Adm +To: GSaha@Ameriquest.com, RRao@Amerquest.com +Subject: $CLEARCASE_OP_KIND: $CLEARCASE_PN +-- +This is a notification that a $CLEARCASE_OP_KIND has occurred: + +Element: $CLEARCASE_PN +Branch: $CLEARCASE_BRTYPE +Operation: $CLEARCASE_OP_KIND +User: $CLEARCASE_USER +View: $CLEARCASE_VIEW_TAG +Comments: + +$CLEARCASE_COMMENT + +For information about recent activity see http://asapcm.ameriquest.net/logs diff --git a/clients/Ameriquest/triggers/ASAP_BUS_REQ_Notify.msg b/clients/Ameriquest/triggers/ASAP_BUS_REQ_Notify.msg new file mode 100644 index 0000000..1f80c95 --- /dev/null +++ b/clients/Ameriquest/triggers/ASAP_BUS_REQ_Notify.msg @@ -0,0 +1,16 @@ +From: ASAP Adm +To: MMonterrey@Ameriquest.com, GSaha@Ameriquest.com, RRao@Amerquest.com, abhijit.bhide@tavant.com +Subject: $CLEARCASE_OP_KIND: $CLEARCASE_PN +-- +This is a notification that a $CLEARCASE_OP_KIND has occurred: + +Element: $CLEARCASE_PN +Branch: $CLEARCASE_BRTYPE +Operation: $CLEARCASE_OP_KIND +User: $CLEARCASE_USER +View: $CLEARCASE_VIEW_TAG +Comments: + +$CLEARCASE_COMMENT + +For information about recent activity see http://asapcm.ameriquest.net/logs diff --git a/clients/Ameriquest/triggers/AddExecute.pl b/clients/Ameriquest/triggers/AddExecute.pl new file mode 100644 index 0000000..5037675 --- /dev/null +++ b/clients/Ameriquest/triggers/AddExecute.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl +################################################################################ +# +# File: AddExecute.pl +# Description: This trigger script simply adds execute permission to an element +# when it is created in Clearcase +# Trigger Type: All element +# Operation: Postop mkelem +# Author: Andrew@DeFaria.com +# Created: Fri Mar 12 10:17:44 PST 2004 +# Language: Perl +# Modifications: +# +# (c) Copyright 2004, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +use strict; +use warnings; + +my $element = $ENV{CLEARCASE_PN}; + +system "cleartool protect -chmod +x \"$element\""; + +exit 0; diff --git a/clients/Ameriquest/triggers/CommentSQLCode.pl b/clients/Ameriquest/triggers/CommentSQLCode.pl new file mode 100644 index 0000000..038794a --- /dev/null +++ b/clients/Ameriquest/triggers/CommentSQLCode.pl @@ -0,0 +1,153 @@ +#!/usr/bin/perl +################################################################################ +# +# File: CommentSQLCode.pl +# Description: This trigger script will gather certain information and write +# that information into the element being checked in in the form +# of a comment. +# +# Here are the requirements as I understand them for the +# trigger that Steve Lipson wants for the SQL +# checkins. Basically he desires a trigger that will +# capture the checkin comment and other information and +# insert that information in the form of a comment at +# the top of the checked in element. This trigger will: +# +# * Be a postop trigger for the checkin action +# * Not be an all element trigger rather it will +# be attached to certain file elements in the +# vob +# * Be made for the vob +# * Only work on file elements - directory +# elements are to be skipped +# * Only work on file elements that have an +# extension of .sql - other elements will be +# skipped +# +# Author: Andrew@DeFaria.com +# Created: Mon Jul 19 10:54:01 PDT 2004 +# Language: Perl +# Modifications:Wed Aug 4 12:41:47 PDT 2004 +# +# (c) Copyright 2004, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +use strict; +use warnings; +use File::Spec; + +# This will be set in the BEGIN block but by putting them here the become +# available for the whole script. +my ( + $abs_path, + $lib_path, + $log_path, + $me, + $triggers_path +); + +BEGIN { + # Extract relative path and basename from script name. + $0 =~ /(.*)[\/\\](.*)/; + + $abs_path = (!defined $1) ? "." : File::Spec->rel2abs ($1); + $me = (!defined $2) ? $0 : $2; + + # Setup paths + $lib_path = "$abs_path/../lib"; + $log_path = "$abs_path/../log"; + $triggers_path = "$abs_path/../triggers"; + + # Add the appropriate path to our modules to @INC array. + unshift (@INC, "$lib_path"); +} # BEGIN + +use TriggerUtils; + +sub getCurrentTime { + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime (time); + $mon++; + $year += 1900; + $hour = "0" . $hour if $hour < 10; + $min = "0" . $min if $min < 10; + return "$mon/$mday/$year\@$hour:$min"; +} # getCurrentTime + +sub parseLSActivity { + my ($activity_id, $activity_title, $activity_owner); + + my @output = `cleartool lsactivity -cact -long`; + + if ($? ne 0 || $#output eq -1) { + clearmsg "You are not set to an activity!"; + exit 1; + } # if + + foreach (@output) { + if (/^activity \"(\S*)\"/) { + $activity_id = $1; + next; + } elsif (/owner: AMERIQUEST\\(\S*)/) { + $activity_owner = $1; + next; + } elsif (/title: (.*)/) { + $_ = $1; + chomp; chop if /\r/; + $activity_title = $_; + next; + } # if + } # foreach + + return ($activity_id, $activity_owner, $activity_title); +} # parseLSActivity + +# Get name of element and its type +my $pname = $ENV{CLEARCASE_PN}; +my $element_type = $ENV{CLEARCASE_ELTYPE}; + +# Skip directories and elements that aren't .sql +exit if $element_type =~ /directory/i || $pname !~ /\.sql$/i; + +# Get comment and user +my $comment = $ENV{CLEARCASE_COMMENT}; +my $userid = $ENV{CLEARCASE_USER}; + +# Format timestamp +my $timestamp = getCurrentTime; + +# Parse output of lsactivity -cact -long +my ($activity_id, $activity_owner, $activity_title) = parseLSActivity; + +# Open up $pname for reading and $pname.trig for writting +open PNAME_IN, $pname + or clearlogmsg "Unable to open $pname for reading - $!\n", exit 1; + +open PNAME_OUT, ">$pname.trig" + or clearlogmsg "Unable to open $pname.trig for writing - $!\n", exit 1; + +# Add comment to top of file +my $activity_str = "$activity_id: $activity_title"; +my $owner_str = $activity_owner =~ /\$userid/i ? "$activity_owner ($userid)" : "$activity_owner"; + +print PNAME_OUT <) { + print PNAME_OUT $_; +} # while + +close PNAME_IN; +close PNAME_OUT; + +# Switch $pname.trig -> $pname +rename "$pname.trig", $pname + or clearlogmsg "Internal error - Unable to rename $pname.trig to $pname", exit 1; + +# Allow checkin to proceed +exit 0; diff --git a/clients/Ameriquest/triggers/LogActivity.pl b/clients/Ameriquest/triggers/LogActivity.pl new file mode 100644 index 0000000..25d3c7a --- /dev/null +++ b/clients/Ameriquest/triggers/LogActivity.pl @@ -0,0 +1,198 @@ +#!/usr/bin/perl +################################################################################ +# +# File: LogActivity.pl +# Description: This trigger will log all activity into a "log" file of HTML +# format. Logfiles are kept per day thus the date appears as +# part of their names. +# +# This script requires one parameter, which is a path to a +# folder where to store the log files. Generally this is a UNC +# path to an area under some web server's DocumentRoot. +# +# Author: Andrew@DeFaria.com +# Created: May 18, 2004 +# Language: Perl +# Modifications: +# +# (c) Copyright 2004, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +use strict; +use warnings; +use File::Spec; + +# This will be set in the BEGIN block but by putting them here the become +# available for the whole script. +my ( + $abs_path, + $me, + $bin_path, + $triggers_path, + $lib_path, + $log_path, + $windows +); + +BEGIN { + # Extract relative path and basename from script name. + $0 =~ /(.*)[\/\\](.*)/; + + $abs_path = (!defined $1) ? "." : File::Spec->rel2abs ($1); + $me = (!defined $2) ? $0 : $2; + + # Check to see if we are running on Windows + $windows = ($^O =~ /MSWin/) ? "yes" : "no"; + + # Setup paths + $bin_path = "$abs_path"; + $triggers_path = "$abs_path/../triggers"; + $lib_path = "$abs_path/../lib"; + $log_path = "$abs_path/../log"; + + # Add the appropriate path to our modules to @INC array. + unshift (@INC, "$lib_path"); +} # BEGIN + +use TriggerUtils; + +use Time::localtime; + +if (!defined $ARGV [0]) { + clearlogmsg "Must specify a logpath!"; + exit 1; +} # if + +my $logpath = $ARGV [0]; + +sub Log { + my $logfile = shift; + my $vob = shift; + my $title = shift; + my $time = shift; + my $user = shift; + my $type = shift; + my $action = shift; + my $path = shift; + my $element = shift; + my $version = shift; + my $comments = shift; + + $logfile = $logpath . "\\" . $logfile; + + if (-e "$logfile") { + my $status = open LOG, ">>$logfile"; + + if (!defined $status) { + clearlogmsg "Unable to open log file $logfile - $!"; + return 1; + } # if + + print LOG < + $time + $user + $type + $action + $path + $element + $version + $comments + +END + } else { + my $status = open LOG, ">>$logfile"; + + if (!defined $status) { + clearlogmsg "Unable to open log file $logfile - $!"; + return 1; + } # if + + print LOG < + + $title + + +

$title

+ + + + + + + + + + + + + + + + + + + + +END + } # if + + close LOG; + + return 0; +} # Log + +# Format $curdate as yyyy-mm-dd and $curtime as hh:mm [AP]m +my $time = localtime; +my $year = $time->year + 1900; +my $month = ($time->mon < 9) ? "0" . ($time->mon + 1) : $time->mon + 1; +my $day = ($time->mday < 10) ? "0" . $time->mday : $time->mday; +my $hours = $time->hour; +my $minutes = ($time->min < 10) ? "0" . $time->min : $time->min; +my $ampm = "Am"; + +if ($hours > 12) { + $ampm = "Pm"; + $hours -= 12; +} elsif ($hours eq 12) { + $ampm = "Pm"; +} # if + +my $curtime = $hours . ":" . $minutes . " $ampm"; +my $curdate = $year . "-" . $month . "-" . $day; + +# Get Clearcase environment variables that we'll need +my $type = $ENV {CLEARCASE_ELTYPE}; +my $user = $ENV {CLEARCASE_USER}; +my $vob = $ENV {CLEARCASE_VOB_PN}; + +# Remove leading "\" from vob, just cause it looks ugly! :-) +$vob =~ s/^\\//; + +my $version = $ENV {CLEARCASE_ID_STR}; + +# $version is N/A for mkelem and rmname +$version = "N/A" if (!defined $version); + +my $comments = $ENV {CLEARCASE_COMMENT}; + +# $comments is N/A for rmname +$comments = "N/A" if (!defined $comments); + +my $pname = $ENV {CLEARCASE_PN}; +my $action = $ENV {CLEARCASE_OP_KIND}; + +my $title = "Activity in vob $vob on $month/$day/$year"; + +# Extract element +my $element = substr ($pname, rindex ($pname, "\\") + 1); +my $path = substr ($pname, rindex ($pname, $vob) + length ($vob) + 1); + +$path = ($path eq $element) ? ".\\" : substr ($path, 0, rindex ($path, $element) - 1); + +my $logfile = "${vob}_$curdate.html"; + +# Create/Add to HTML logfile. +exit (Log $logfile, $vob, $title, $curtime, $user, $type, $action, $path, $element, $version, $comments); diff --git a/clients/Ameriquest/triggers/NoPBLs.pl b/clients/Ameriquest/triggers/NoPBLs.pl new file mode 100644 index 0000000..d7d1281 --- /dev/null +++ b/clients/Ameriquest/triggers/NoPBLs.pl @@ -0,0 +1,65 @@ +#!/usr/bin/perl +################################################################################ +# +# File: NoPBLs.pl +# Description: This trigger stops all users except for vobadm and Steve Lipson +# (userid to be specified) from checking in PBLs which are +# PowerBuilder libraries and should never be checked into a vob. +# Why Steve Lipson would want this capability is unknown. +# +# Author: Andrew@DeFaria.com +# Created: May 18, 2004 +# Language: Perl +# Modifications: +# +# (c) Copyright 2004, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +use strict; +use warnings; +use File::Spec; + +# This will be set in the BEGIN block but by putting them here the become +# available for the whole script. +my ( + $abs_path, + $me, + $bin_path, + $triggers_path, + $lib_path, + $log_path, + $windows +); + +BEGIN { + # Extract relative path and basename from script name. + $0 =~ /(.*)[\/\\](.*)/; + + $abs_path = (!defined $1) ? "." : File::Spec->rel2abs ($1); + $me = (!defined $2) ? $0 : $2; + + # Check to see if we are running on Windows + $windows = ($^O =~ /MSWin/) ? "yes" : "no"; + + # Setup paths + $bin_path = "$abs_path"; + $triggers_path = "$abs_path/../triggers"; + $lib_path = "$abs_path/../lib"; + $log_path = "$abs_path/../log"; + + # Add the appropriate path to our modules to @INC array. + unshift (@INC, "$lib_path"); +} # BEGIN + +use TriggerUtils; + +my $steve_lipson = "sl020353"; +my $user = $ENV {CLEARCASE_USER}; +my $pname = $ENV {CLEARCASE_PN}; + +if ($pname =~ /\.pbl$/i and lc ($user) !~ $steve_lipson) { + clearmsg "Check in's of pbl's are not allowed except for administrators"; + exit 1; +} # if + +exit 0; diff --git a/clients/Ameriquest/triggers/Permissions.pl b/clients/Ameriquest/triggers/Permissions.pl new file mode 100644 index 0000000..476353f --- /dev/null +++ b/clients/Ameriquest/triggers/Permissions.pl @@ -0,0 +1,228 @@ +#!/usr/bin/perl +################################################################################ +# +# File: Permissions.pl +# Description: This trigger script implements additional permissions checking. +# The general idea is to open up permissions at the group level +# and to control who gets to checkout elements at the folder +# level. You do this by making an element named +# $permissions_element which contains group names of which +# groups have "checkout" permissions in that folder downward. +# Author: Andrew@DeFaria.com +# Created: Mon Jul 19 10:54:01 PDT 2004 +# Language: Perl +# Modifications: +# +# (c) Copyright 2004, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +use strict; +use warnings; +use File::Spec; + +# This will be set in the BEGIN block but by putting them here the become +# available for the whole script. +my ( + $abs_path, + $lib_path, + $log_path, + $me, + $triggers_path +); + +BEGIN { + # Extract relative path and basename from script name. + $0 =~ /(.*)[\/\\](.*)/; + + $abs_path = (!defined $1) ? "." : File::Spec->rel2abs ($1); + $me = (!defined $2) ? $0 : $2; + + # Setup paths + $lib_path = "$abs_path/../lib"; + $log_path = "$abs_path/../log"; + $triggers_path = "$abs_path/../triggers"; + + # Add the appropriate path to our modules to @INC array. + unshift (@INC, "$lib_path"); +} # BEGIN + +use TriggerUtils; + +# Name of permissions element to search for +my $permissions_element = ".perms"; + +# Trigger environment variables used +my $pname = $ENV{CLEARCASE_PN}; +my $user = $ENV{CLEARCASE_USER}; +my $vob = $ENV{CLEARCASE_VOB_PN}; + +sub ParentDir { + my $path = shift; + + $path =~ m/(.*)[\/\\].*/; + + return $1; +} # ParentDir + +# Returns the current group owner of the vob. This is the first group listed, not the' +# "Additional groups". +sub GetGroupOwner { + my $vob = shift; + + my @output = `cleartool describe vob:$vob 2>&1`; + + foreach (@output) { + chomp; chop if /\r/; + if (/group AMERIQUEST\\(.*)/) { + return $1; + } # if + } # foreach + + return "Unknown"; +} # GetGroupOwner + +# Returns the primary group using creds +sub GetPrimaryGroup { + my @output = `"C:\\Program Files\\Rational\\Clearcase\\etc\\utils\\creds.exe" 2>&1`; + + foreach (@output) { + chomp; chop if /\r/; + if (/Primary group: AMERIQUEST\\(\S*).*/) { + return $1; + } # if + } # foreach + + return "Domain Users"; +} # GetPrimaryGroup + +# Parsed the $permissions_element returning a list of permitted groups. +sub Parse { + my $permissions_element = shift; + + open PERMISSIONS_ELEMENT, $permissions_element + or clearlogmsg "Unable to open $permissions_element - $!\n", exit 1; + + my @lines = ; + my @tidy_lines; + + foreach (@lines) { + chomp; chop if /\r/; + next if $_ eq ""; + push @tidy_lines, $_; + } # foreach + + return @tidy_lines; +} # Parse + +# Compare the two string arrays and return 1 if there are any matches. +sub IsAMember { + my $set1 = shift; + my $set2 = shift; + + # Convert two array references to actual arrays + my @set1 = @{$set1}; + my @set2 = @{$set2}; + + foreach my $item1 (@set1) { + foreach my $item2 (@set2) { + return 1 if $item1 eq $item2; + } # foreach + } # foreach + + return 0; +} # IsAMember + +# Returns an array of (AMERIQUEST) group names for the user using creds. +sub GetUserGroups { + my @output = `"C:\\Program Files\\Rational\\Clearcase\\etc\\utils\\creds.exe" 2>&1`; + my @groups; + my $found = 0; + + foreach (@output) { + chomp; chop if /\r/; + + # We should first see the Primary Grou + if (/Primary group: AMERIQUEST\\(.*) \(/) { + push @groups, $1; + } # if + + # When we hit the "Groups:" line then what follows is a list of groups + if (/^Groups:/) { + $found = 1; + next + } # if + + # Select only those that are specifically in the AMERIQUEST domain + if ($found eq 1 and /\s*AMERIQUEST\\(.*) \(/) { + push @groups, $1; + } # if + } # foreach + + return @groups +} # GetUserGroups + +# This routine will check to see if any of the user's groups are in the +# $permissions_element(s) by recursing up the directory looking for +# $permissions_element(s) then comparing those groups to the user's groups. +sub Permitted { + my $vob = shift; + my $pname = shift; + my @user_groups = @_; + + # User may be attemptign to Add to Source Control in the current + # directory and have permissions to do so. When Add to Source + # Control runs it checks out the parent directory. The user + # typically will NOT have permissions to check out the parent + # directory! So for directory elements first check if the user is + # permitted as per $pname/$permissions_element BEFORE traversing up + # to the parent directory. + my @permitted_groups; + my $element_type = $ENV{CLEARCASE_ELTYPE}; + + if ($element_type =~ /directory/i) { + if (-e "$pname/$permissions_element") { + @permitted_groups = Parse ("$pname/$permissions_element"); + return 1 if (IsAMember (\@user_groups, \@permitted_groups)); + } # if + } # if + + # Get parent directory + $pname = ParentDir $pname; + + # Exhausted $pname + return 0 if !defined $pname; + + if (-e "$pname/$permissions_element") { + @permitted_groups = Parse ("$pname/$permissions_element"); + return 1 if (IsAMember (\@user_groups, \@permitted_groups)); + } # if + + # Recurse up to parent directory + return Permitted ($vob, $pname, @user_groups); +} # Permitted + +# Main +my $vob_group_owner = GetGroupOwner $vob; +my $group = GetPrimaryGroup; +my @user_groups = GetUserGroups; +my $msg; + +if ($vob_group_owner eq $group) { + # Vob group openers are always permitted + exit 0; +} elsif ($pname =~ m/$permissions_element$/) { + # User trying to check out the $permissions_element! + $msg .= "Only members of the vob's initial group owners,\\n"; + $msg .= "$vob_group_owner, may checkout the $permissions_element element!"; + clearmsg $msg; + exit 1; +} elsif (Permitted ($vob, $pname, @user_groups)) { + exit 0; +} else { + $msg .= "The userid of $user is not a member of a group who is\\n"; + $msg .= "permitted to check out elements from the folder\\n"; + $msg .= ParentDir $pname; + $msg .= " of the $vob vob."; + clearmsg $msg; + exit 1; +} # if diff --git a/clients/Ameriquest/triggers/defaria_notify.msg b/clients/Ameriquest/triggers/defaria_notify.msg new file mode 100644 index 0000000..48efe5b --- /dev/null +++ b/clients/Ameriquest/triggers/defaria_notify.msg @@ -0,0 +1,16 @@ +From: ASAP Adm +To: ADeFaria@Ameriquest.net +Subject: $CLEARCASE_OP_KIND: $CLEARCASE_PN +-- +This is a notification that a $CLEARCASE_OP_KIND has occurred: + +Element: $CLEARCASE_PN +Branch: $CLEARCASE_BRTYPE +Operation: $CLEARCASE_OP_KIND +User: $CLEARCASE_USER +View: $CLEARCASE_VIEW_TAG +Comments: + +$CLEARCASE_COMMENT + +For information about recent activity see http://asapcm.ameriquest.net/logs diff --git a/clients/GD/FSMon/Filesystem.pm b/clients/GD/FSMon/Filesystem.pm new file mode 100644 index 0000000..3d230d6 --- /dev/null +++ b/clients/GD/FSMon/Filesystem.pm @@ -0,0 +1,324 @@ +=pod + +=head2 NAME $RCSfile: FileSystem.pm,v $ + +Object oriented interface to filesystems + +=head2 VERSION + +=over + +=item Author: + +Andrew DeFaria + +=item Revision: + +$Revision: $ + +=item Created: + +Thu Dec 11 10:39:12 MST 2008 + +=item Modified: + +$Date:$ + +=back + +=head2 SYNOPSIS + +This module implements a FileSystem object. + + $fs = new FileSystem ("hosta"); + + while ($fs->filesystem) { + display "Filesystem: $_"; + display "\tSize:\t$fs{$_}->size"; + display "\tUsed:$fs{$_}->used"; + display "\tFree:$fs{$_}->free"; + display "\tUsed %:$fs{$_}->usedPct"; + display "\tMounted on:$fs{$_}->mount"; + } # while + +=head2 DESCRIPTION + +Filesystem creates a filesystem object that encapsulates information +about the file system as a whole. + +=head2 ROUTINES + +The following routines are exported: + +=over + +=cut + +use strict; +use warnings; + +package Filesystem; + +use base "Exporter"; + +use OSDep; +use Display; +use Utils; +use Rexec; + +=pod + +=head3 new () + +Construct a new Filesystem object. The following OO style arguments are +supported: + +Parameters: + +=for html
+ +=over + +=item none + +Returns: + +=for html
+ +=over + +=item Filesystem object + +=back + +=for html
+ +=cut + +sub new ($;$$$$) { + my ($class, $system, $ostype, $username, $password, $prompt, $shellstyle) = @_; + + # Set prompt if not passed in + $prompt ||= $Rexec::default_prompt; + + # Connect to remote machine + my $remote = new Rexec ( + host => $system, + username => $username, + password => $password, + prompt => $prompt, + shellstyle => $shellstyle, + ); + + unless ($remote) { + error "Unable to connect to $system"; + + return undef; + } # if + + my (@fs, %fs); + + # Sun is so braindead! + if ($ostype eq "Unix") { + foreach ("ufs", "vxfs") { + my $cmd = "/usr/bin/df -k -F $_"; + + my @unixfs = $remote->exec ($cmd); + + if ($remote->status != 0) { + error ("Unable to determine fsinfo on $system ($cmd)\n" . join ("\n", @fs));; + return undef; + } # if + + # Skip heading + shift @unixfs; + + for (my $i = 0; $i < scalar @unixfs; $i++) { + my (%fsinfo, $firstField); + + # Trim leading and trailing spaces + $unixfs[$i] =~ s/^\s+//; + $unixfs[$i] =~ s/\s+$//; + + my @fields = split /\s+/, $unixfs[$i]; + + if (scalar @fields == 1) { + $fsinfo{fs} = $fields[0]; + $firstField = 0; + $i++; + + # Trim leading and trailing spaces + $unixfs[$i] =~ s/^\s+//; + $unixfs[$i] =~ s/\s+$//; + + @fields = split /\s+/, $unixfs[$i];; + } else { + $fsinfo{fs} = $fields[0]; + $firstField = 1; + } #if + + $fsinfo{size} = $fields[$firstField] * 1024; + $fsinfo{used} = $fields[$firstField + 1] * 1024; + $fsinfo{free} = $fields[$firstField + 2] * 1024; + $fsinfo{reserve} = $fsinfo{size} - $fsinfo{used} - $fsinfo{free}; + + $fs{$fields[$firstField + 4]} = \%fsinfo; + } # for + } # foreach + } elsif ($ostype eq "Linux") { + foreach ("ext3") { + my $cmd = "/bin/df --block-size=1 -t $_"; + + my @linuxfs = $remote->exec ($cmd); + + if ($remote->status != 0) { + error ("Unable to determine fsinfo on $system ($cmd)\n" . join ("\n", @fs));; + return undef; + } # if + + # Skip heading + shift @linuxfs; + + foreach (@linuxfs) { + my %fsinfo; + my @fields = split; + + $fsinfo{fs} = $fields[0]; + $fsinfo{size} = $fields[1]; + $fsinfo{used} = $fields[2]; + $fsinfo{free} = $fields[3]; + $fsinfo{reserve} = $fsinfo{size} - $fsinfo{used} - $fsinfo{free}; + + $fs{$fields[5]} = \%fsinfo; + } # foreach + } # foreach + } else { + error "Can't handle $ostype", 1; + } # if + + bless \%fs, $class; +} # new + +=pod + +=head3 mounts () + +Returns an array of mount points + +Parameters: + +=for html
+ +=over + +=item none + +None + +=back + +=for html
+ +Returns: + +=for html
+ +=over + +=item Array of mount points + +=back + +=for html
+ +=cut + +sub mounts () { + my ($self) = shift; + + return keys %{$self} +} # mounts + +=pod + +=head3 getFSInfo ($mount) + +Returns a hash of filesystem info for a mount point + +Parameters: + +=for html
+ +=over + +=item $mount: Mount point + +None + +=back + +=for html
+ +Returns: + +=for html
+ +=over + +=item Hash of filesystem info + +=back + +=for html
+ +=cut + +sub getFSInfo ($) { + my ($self, $mount) = @_; + + return %{$self->{$mount}}; +} # getFSInfo + +1; + +=back + +=head2 CONFIGURATION AND ENVIRONMENT + +None + +=head2 DEPENDENCIES + + Display + OSDep + Utils + +=head2 INCOMPATABILITIES + +None yet... + +=head2 BUGS AND LIMITATIONS + +There are no known bugs in this module. + +Please report problems to Andrew DeFaria (Andrew@ClearSCM.com). + +=head2 LICENSE AND COPYRIGHT + +This Perl Module is freely available; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This Perl Module is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License (L) for more +details. + +You should have received a copy of the GNU General Public License +along with this Perl Module; if not, write to the Free Software Foundation, +Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +reserved. + +=cut diff --git a/clients/GD/FSMon/Fonts/GeosansLight.ttf b/clients/GD/FSMon/Fonts/GeosansLight.ttf new file mode 100644 index 0000000000000000000000000000000000000000..055932aac8de6861ab4e3a8f54e54d1f170101de GIT binary patch literal 60072 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 literal 0 HcmV?d00001 diff --git a/clients/GD/FSMon/Fonts/Silkscreen.ttf b/clients/GD/FSMon/Fonts/Silkscreen.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ae4425ddabf65d729318736cab2435f47ebe45fa GIT binary patch literal 16172 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 literal 0 HcmV?d00001 diff --git a/clients/GD/FSMon/Fonts/pf_arma_five.ttf b/clients/GD/FSMon/Fonts/pf_arma_five.ttf new file mode 100644 index 0000000000000000000000000000000000000000..db04ec3b0480d81f4e866917f482d5d9619522b1 GIT binary patch literal 21936 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 literal 0 HcmV?d00001 diff --git a/clients/GD/FSMon/Fonts/tahoma.ttf b/clients/GD/FSMon/Fonts/tahoma.ttf new file mode 100644 index 0000000000000000000000000000000000000000..59b14a2d2d45b78eecbb2f22e1d2e084678e8d94 GIT binary patch literal 383804 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 O@`a}xV@&j7=>7#Q^<=;R literal 0 HcmV?d00001 diff --git a/clients/GD/FSMon/FsmonDB.php b/clients/GD/FSMon/FsmonDB.php new file mode 100644 index 0000000..d39021b --- /dev/null +++ b/clients/GD/FSMon/FsmonDB.php @@ -0,0 +1,181 @@ +N/A"; + +function dbError ($msg, $statement) { + $errno = mysql_errno (); + $errmsg = mysql_error (); + + print <<ERROR: $msg +Error #$errno:
+

$errmsg
+SQL Statement:
+
$statement
+END; + + exit ($errno); +} // dbError + +function openDB () { + global $dbserver, $userid, $password, $dbname; + + $db = mysql_connect ($dbserver, $userid, $password) + or dbError (__FUNCTION__ . ": Unable to connect to database server $dbserver", "Connect"); + + mysql_select_db ($dbname) + or dbError (__FUNCTION__ . ": Unable to select the $dbname database", "$dbname"); +} // openDB + +function getFSInfo ($system = "", $mount = "", $period = "daily") { + $sysCondition = (isset ($system)) ? "where sysname = \"$system\"" : ""; + $mountCondition = (isset ($mount)) ? " and mount = \"$mount\"" : ""; + + if (!($period == "hourly" or + $period == "daily" or + $period == "weekly" or + $period == "monthly")) { + error ("Invalid period - $period - specified", 1); + } // if + + $statement = << $maxUsed) { + $maxUsed = $row["used"]; + $line["size"] = $row["size"]; + $line["used"] = $row["used"]; + $line["free"] = $row["free"]; + $line["reserve"] = $row["reserve"]; + } else { + continue; + } // if + } elseif ($period == "weekly") { + error ("Weekly not handled yet", 1); + } elseif ($period == "monthly") { + $thisPeriod = substr ($row["timestamp"], 0, 7); + if ($lastPeriod == "") { + $lastPeriod = $thisPeriod; + } elseif ($lastPeriod == $thisPeriod) { + continue; + } // if + + if ($row["used"] > $maxUsed) { + $maxUsed = $row["used"]; + $line["size"] = $row["size"]; + $line["used"] = $row["used"]; + $line["free"] = $row["free"]; + $line["reserve"] = $row["reserve"]; + } else { + continue; + } // if + } // if + + array_push ($data, $line); + } // while + + return $data; +} // getFSInfo + +function getSystem ($system = "") { + $statement = "select * from system"; + + if (isset ($system) and $system != "") { + $statement .= " where name = \"$system\""; + } // if + + $result = mysql_query ($statement) + or DBError ("Unable to execute query: ", $statement); + + $data = array (); + + while ($row = mysql_fetch_array ($result, MYSQL_ASSOC)) { + array_push ($data, $row); + } // while + + return $data; +} // getSystem + +function getMounts ($system) { + $statement = "select mount from filesystems where sysname = \"$system\" order by mount"; + + $result = mysql_query ($statement) + or DBError ("Unable to execute query: ", $statement); + + $data = array (); + + while ($row = mysql_fetch_array ($result)) { + array_push ($data, $row["mount"]); + } // while + + return $data; +} // getMounts +?> diff --git a/clients/GD/FSMon/FsmonDB.pm b/clients/GD/FSMon/FsmonDB.pm new file mode 100644 index 0000000..786f4cd --- /dev/null +++ b/clients/GD/FSMon/FsmonDB.pm @@ -0,0 +1,700 @@ +=pod + +=head2 NAME $RCSfile: FsmonDB.pm,v $ + +Object oriented interface to filesystems + +=head2 VERSION + +=over + +=item Author: + +Andrew DeFaria + +=item Revision: + +$Revision: $ + +=item Created: + +Thu Dec 11 10:39:12 MST 2008 + +=item Modified: + +$Date:$ + +=back + +=head2 SYNOPSIS + +=head1 SYNOPSIS + + use FsmonDB; + + my $username = "fsmonadm"; + my $password = ""; + + my $fsmondb = new FsmonDB ($username, $password); + + my ($errno, $errmsg) = $fsmondb->addSystem ( + name => hostname, + owner => "root", + description => "Database server", + ostype => "Unix", + osversion => `uname -a`, + monitorAllFS => 1, + ); + + $status = $fsmondb->fsSnapshot (hostname); + +=head2 DESCRIPTION + +Filesystem creates a filesystem object that encapsulates information +about the file system as a whole. + +=head2 ROUTINES + +The following routines are exported: + +=over + +=cut + +use strict; +use warnings; + +package FsmonDB; + +use Carp; +use DBI; + +use DateUtils; +use Display; +use Filesystem; + +############################################################################ +# +# insert: Construct SQL insert statement based on passed in table +# name and hash values. +# +# Parms: +# table Table name +# record Name value hash +# +# Returns: +# status ($errno, $errmsg) +# +############################################################################ +sub insert ($%) { + my ($self, $table, %values) = @_; + + my $first = 1; + my $fields; + my $values; + + foreach (keys %values) { + if ($first) { + $first = 0; + } else { + $fields .= ","; + $values .= ","; + } # if + + if (!defined $_) { + $values .= "NULL"; + } else { + $fields .= $_; + $values .= "\"" . quotemeta ($values{$_}) . "\""; + } # if + } # foreach + + my $statement = "insert into $table ($fields) values ($values)"; + + $self->{db}->do ($statement) + or return $self->_dberror ("Unable to add system", $statement); + + return (0, undef); +} # insert + +############################################################################ +# +# _dberror: Output the DB error message and exit (Internal) +# +# Parms: +# msg User defined message to output +# statement SQL Statement attempted (optional) +# +# Returns: +# Nothing +# +############################################################################ +sub _dberror ($;$) { + my ($self, $msg, $statement) = @_; + + my $caller = (caller (1))[3]; + + my $returnMsg = "$caller: DBError: " + . $msg + . "\nError #" + . $self->{db}->err + . " " + . $self->{db}->errstr; + + $returnMsg .= "\nSQL Statement: $statement\n" if $statement; + + return ($self->{db}->err, $returnMsg); +} # _dberror + +############################################################################ +# +# _exists: Return 1 if the value exists in the table otherwise 0 +# +# Parms: +# table Name of table to search +# column Column name to search +# value Value to look for +# column2 Secondary column to search for +# value2 Secondary value to search for +# +# Returns: +# 1 if found, 0 if not +# +############################################################################ +sub _exists ($$$;$$) { + my ($self, $table, $column, $value, $column2, $value2) = @_; + + my $statement = "select count(*) from $table where $column = \"" + . quotemeta ($value) + . "\""; + + $statement .= " and $column2 = \"" + . quotemeta ($value2) + . "\"" if $column2; + + my $sth; + + unless ($sth = $self->{db}->prepare ($statement)) { + my ($errNo, $errMsg) = $self->_dberror ("Unable to prepare statement", $statement); + display $errMsg; + return 0; + } # unless + + unless ($sth->execute) { + my ($errNo, $errMsg) = $self->_dberror ("Unable to execute statement", $statement); + display $errMsg; + return 0; + } # unless + + my @row = $sth->fetchrow_array; + + $sth->finish; + + if ($row[0]) { + return $row[0] + } else { + return 0; + } # if +} # _exists + +############################################################################ +# +# _count: Returns the number of entries in a table that qualify for +# the given condition. +# +# Parms: +# table Name of table to search +# id condition (Default: All entries in the table) +# +# Returns: +# Count of qualifying entries +# +############################################################################ +sub _count ($;$) { + my ($self, $table, $condition) = @_; + + $condition = $condition ? "where $condition" : ""; + + my $statement = "select count(*) from $table $condition"; + + my $sth; + + unless ($sth = $self->{db}->prepare ($statement)) { + my ($errNo, $errMsg) = $self->_dberror ("Unable to prepare statement", $statement); + display $errMsg; + return -1; + } # unless + + unless ($sth->execute) { + my ($errNo, $errMsg) = $self->_dberror ("Unable to execute statement", $statement); + display $errMsg; + return -1; + } # unless + + my @row = $sth->fetchrow_array; + + $sth->finish; + + if ($row[0]) { + return $row[0] + } else { + return 0; + } # if +} # _count + +=pod + +=head3 new () + +Construct a new FsmonDB object. The following OO style arguments are +supported: + +Parameters: + +=for html
+ +=over + +=item none + +Returns: + +=for html
+ +=over + +=item FsmonDB object + +=back + +=for html
+ +=cut + +sub new (;$$) { + my ($class, $username, $password) = @_; + + $username = $username ? $username : "fsmon"; + $password = $password ? $password : "fsmon"; + + my $dbname = $ENV{FSMON_DBNAME} + ? $ENV{FSMON_DBNAME} + : "fsmon"; + my $dbserver = $ENV{FSMON_DBSERVER} + ? $ENV{FSMON_DBSERVER} + : "seast1"; + my $dbdriver = "mysql"; + + my $db = DBI->connect ("DBI:$dbdriver:$dbname:$dbserver", $username, + $password, {PrintError => 0}) + or croak "Unable to connect to $dbname database as $username"; + + return bless { + db => $db, + username => $username, + password => $password, + }, $class; +} # new + +=pod + +=head3 addSystem (%system) + +Add a system record + +Parameters: + +=for html
+ +=over + +%system is a hash containing the following keys: + +=item $name (required) + +Name of the system + +=item $owner (optional) + +Person or persons responsible for this system + +=item $description (optional) + +Description of this system + +=item $ostype (required) + +An enumeration of "Linux", "Unix" or "Windows" (default Linux) + +=item $osversion (optional) + +String representing an OS version + +Returns: + +=for html
+ +=over + +=item FsmonDB object + +=back + +=for html
+ +=cut + +sub addSystem (%) { + my ($self, %record) = @_; + + return $self->insert ("system", %record); +} # addSystem + +=pod + +=head3 getSystem ($system) + +Get a system record + +Parameters: + +=for html
+ +=over + +=item $system (option) + +Name of the system to return information about. If not specified then +getSystem returns all systems + +Returns: + +=for html
+ +=over + +=item If $system was specified, a hash of that system's +information. If $system is not specified then an array of hashes +containing information on all systems. + +=back + +=for html
+ +=cut + +sub getSystem (;$) { + my ($self, $system) = @_; + + my ($statement, $sth); + + if ($system) { + $statement = "select * from system where name = \"$system\""; + } else { + $statement = "select name from system"; + } # unless + + unless ($sth = $self->{db}->prepare ($statement)) { + my ($errno, $errmsg) = $self->_dberror ("Unable to prepare statement", $statement); + error $errmsg, $errno; + } # unless + + unless ($sth->execute) { + my ($errno, $errmsg) = $self->_dberror ("Unable to execute statement", $statement); + error $errmsg, $errno; + } # unless + + if ($system) { + return %{my $row = $sth->fetchrow_hashref}; + } else { + my @records; + + while (my @record = $sth->fetchrow_array) { + push @records, pop @record; + } # while + + return @records; + } # if +} # addSystem + +=pod + +=head3 addFilesystem ($system, $mount) + +Add monitoring of a filesytem identified by $mount from a $system + +Parameters: + +=for html
+ +=over + +=item $system (required) + +Name of the system that this filesystem is local to + +=item mount (optional) + +Mount point for this file system. If undef then add all local file systems. + +Returns: + +=for html
+ +=over + +=item ($errno, $errmsg) + +=back + +=for html
+ +=cut + +sub addFilesystem ($;$) { + my ($self, $system, $mount) = @_; + + my $fs = new Filesystem; + + foreach ($fs->mounts ()) { + my %fsinfo = $fs->getFSInfo ($_); + my %filesystem = ( + "sysname" => $system, + "mount" => $_, + "fs" => $fsinfo{fs}, + ); + my ($errno, $errmsg); + + if ($mount) { + if ($mount eq $_) { + ($errno, $errmsg) = $self->insert ("filesystems", %filesystem); + + return ($errno, $errmsg) if $errno != 0; + } # if + } else { + ($errno, $errmsg) = $self->insert ("filesystems", %filesystem); + + return ($errno, $errmsg) if $errno != 0; + } # if + } # foreach + + return (0, undef); +} # addFilesystem + +=pod + +=head3 addSnapshot (%snapshot) + +Add a snapshot record of a filesystem + +Parameters: + +=for html
+ +=over + +%snapshot is a hash containing the following keys: + +=item sysname (required) + +Name of the system that this filesystem is local to + +=item mount (required) + +Mount point for this file system + +=item timestamp (required) + +Timestamp representing the time that the snapshot of the filesystem +was taken. + +=item size (optional) + +Total size of the filesystem in bytes + +=item used (optional) + +Number of bytes of used space + +=item free (optional) + +Number of bytes free or available for use + +=item reserve (optional) + +Number of bytes held in reserve + +Returns: + +=for html
+ +=over + +=item ($errno, $errmsg) + +=back + +=for html
+ +=cut + +sub addSnapshot (%) { + my ($self, %snapshot) = @_; + + return $self->insert ("fs", %snapshot); +} # addSnapshot + +=pod + +=head3 snapshot ($system) + +Take a snapshot of all configured file systems for a given system + +Parameters: + +=for html
+ +=over + +=item $system + +Name of the system to snapshot + +Returns: + +=for html
+ +=over + +=item ($errno, $errmsg) + +=back + +=for html
+ +=cut + +sub snapshot ($;$) { + my ($self, $system) = @_; + + my %system = $self->getSystem ($system); + my $fs = new Filesystem ( + $system, + $system{ostype}, + $system{username}, + $system{password}, + qr "$system{prompt}", + $system{shellstyle}, + ); + + if ($fs) { + foreach ($fs->mounts ()) { + my %fsinfo = $fs->getFSInfo ($_); + my %fs; + + # Format record + $fs{sysname} = $system; + $fs{mount} = $_; + $fs{timestamp} = Today2SQLDatetime; + $fs{size} = $fsinfo{size}; + $fs{used} = $fsinfo{used}; + $fs{free} = $fsinfo{free}; + $fs{reserve} = $fsinfo{reserve}; + + my ($errno, $errmsg) = $self->addSnapshot (%fs); + + return ($errno, $errmsg) if $errno != 0; + } # foreach + } # if + + return (0, ""); +} # snapshot + +1; + +=head1 NAME + +FsmonDB - Access routines to the fsmon SQL database + +=head1 VERSION + +Version 1.0 + +=head1 DESCRIPTION + +This module provides for access routines to the fsmon SQL database. + +=head1 METHODS + +=head2 new ($username, $password) + +Opens the fsmon SQL database for the specified $username and +$password and returns a FsmonDB object + +Parameters: + +=over + +=item username: + +Username to connect to the database. At this time "fsmonadm" is the R/W +user and "fsmon" (password "reader") has R/O access (Default: +fsmon). + +=item password: + +Password to use. (Default: "fsmon"). + +=item Returns FsmonDB object + +=back + +=head2 addSystem (%testrun) + +Adds the system record. Pass in a hash of field name/value pairs. + +=over + +=item ($errno, $errmsg) + +=back + +=back + +=head2 CONFIGURATION AND ENVIRONMENT + +None + +=head2 DEPENDENCIES + + ... + +=head2 INCOMPATABILITIES + +None yet... + +=head2 BUGS AND LIMITATIONS + +There are no known bugs in this module. + +Please report problems to Andrew DeFaria (Andrew@ClearSCM.com). + +=head2 LICENSE AND COPYRIGHT + +This Perl Module is freely available; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This Perl Module is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License (L) for more +details. + +You should have received a copy of the GNU General Public License +along with this Perl Module; if not, write to the Free Software Foundation, +Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +reserved. + +=cut diff --git a/clients/GD/FSMon/FsmonDB.sql b/clients/GD/FSMon/FsmonDB.sql new file mode 100644 index 0000000..0d0e091 --- /dev/null +++ b/clients/GD/FSMon/FsmonDB.sql @@ -0,0 +1,81 @@ +-------------------------------------------------------------------------------- +-- +-- File: FsmonDB.sql +-- Revision: 0.1 +-- Description: Database definition for fsmon +-- Author: Andrew@ClearSCM.com +-- Created: Thu Dec 11 13:43:06 MST 2008 +-- Modified: +-- Language: SQL +-- +-- Copyright (c) 2008, General Dynamics, all rights reserved +-- +-------------------------------------------------------------------------------- +-- Warning: The following line will delete the old database! +drop database if exists fsmon; + +-- Create a new database +create database fsmon; + +-- Now let's focus on this new database +use fsmon; + +-- system: Contains information about the various machines that we are +-- monitoring file systems on + +create table system ( + name varchar(255) not null, + owner tinytext, + description text, + ostype enum ( + "Linux", + "Unix", + "Windows" + ) not null, + osversion tinytext, + username tinytext, + password tinytext, + prompt tinytext, + shellstyle enum ( + "sh", + "csh" + ) not null, + + primary key (name) +) engine = InnoDB; + +-- filesystems: Describes the filesystems for a system +create table filesystems ( + sysname varchar(255) not null, + mount varchar(255) not null, + fs tinytext not null, + + primary key (sysname, mount) +) engine = InnoDB; + +-- fs: Contains a snapshot reading of a filesystem at a given date and time +create table fs ( + sysname varchar(255) not null, + mount varchar(255) not null, + timestamp datetime not null, + size bigint, + used bigint, + free bigint, + reserve bigint, + + primary key (sysname, mount, timestamp), + foreign key (sysname, mount) + references filesystems (sysname, mount) + on delete cascade + on update cascade +) engine = InnoDB; + +grant all privileges + on fsmon.* + to fsmonadm@"%" + identified by "fsmonadm" with grant option; + +grant select + on fsmon.* + to fsmon@"%" + identified by "fsmon"; diff --git a/clients/GD/FSMon/Fsutils.php b/clients/GD/FSMon/Fsutils.php new file mode 100644 index 0000000..d5166cd --- /dev/null +++ b/clients/GD/FSMon/Fsutils.php @@ -0,0 +1,105 @@ +DEBUG: $msg
"; + } // if +} // debug + +function dumpObject ($object) { + print "
";
+  print_r ($object);
+  print "
"; +} // dumpObject + +function error ($msg, $errno = 0) { + print "

ERROR: $msg"; + + if ($errno != 0) { + print " ($errno)

"; + exit; + } else { + print "

"; + } // if +} // error + +function banner () { + return $banner; +} // banner + +function YMD2MDY ($date) { + return substr ($date, 5, 2) . "/" . + substr ($date, 8, 2) . "/" . + substr ($date, 0, 4); +} // YMD2MDY + +function MDY2YMD ($date) { + return substr ($date, 6, 4) . "-" . + substr ($date, 0, 2) . "-" . + substr ($date, 3, 2); +} // MDY2YMD + +function copyright () { + $year = date ("Y"); + + $thisFile = "$_SERVER[DOCUMENT_ROOT]/$_SERVER[PHP_SELF]"; + $lastModified = date ("F d Y @ g:i a", filemtime ($thisFile)); + + $copyright .= << +Fsmon Version +END; + $copyright .= VERSION; + $copyright .= <<Last Modified: $lastModified
+Copyright $year ©
ClearSCM, Inc., all rights reserved
+Home + +END; + + return $copyright; +} // copyright + +function Today2SQLDatetime () { + return date ("Y-m-d H:i:s"); +} // Today2SQLDatetime + +function getPeriods () { + return array ( + "hourly", + "daily", + "weekly", + "monthly" + ); +} // getPeriods + +function getScales () { + return array ( + "byte", + "kbyte", + "meg", + "gig" + ); +} // getScales +?> \ No newline at end of file diff --git a/clients/GD/FSMon/fsmon b/clients/GD/FSMon/fsmon new file mode 100755 index 0000000..b3307ba --- /dev/null +++ b/clients/GD/FSMon/fsmon @@ -0,0 +1,83 @@ +#!/bin/env /usr/local/bin/perl +################################################################################ +# +# File: fsmon +# Revision: 0.1 +# Description: Monitor filesystem growth +# +# Author: Andrew@ClearSCM.com +# Created: Thu Dec 11 10:39:12 MST 2008 +# Modified: +# Language: Perl +# +# (c) Copyright 2008, ClearSCM, Inc., all rights reserved. +# +################################################################################# +use strict; +use warnings; + +use FindBin; +use Getopt::Long; +use Net::Domain qw(hostname); + +use lib ($FindBin::Bin, "/cleartrig/ent/SNSD/muos/ccadm_tools/vobs/ranccadm/scripts/lib"); + +use Display; +use Rexec; +use Filesystem; +use FsmonDB; + +# This is a non-standard, but commonly used prompt around here. For +# EAST systems they use a terminator of "]$" as in "[p6258c@ceast1 +# p6258c]$ " however on ranray it's more like "[ranray/home/pwit] +# ". So we look for both. +use constant PROMPT => qr'(\]\$|\] $)'; + +my %_opts; + +sub usage (;$) { + my ($msg) = $_; + + my $usage = "ERROR: $msg\n\n" if $msg; + + $usage .= < sub { set_verbose }, + debug => sub { set_debug }, + usage => sub { usage; exit 0 }, +); + +# Connect to database +my $fsmondb = new FsmonDB ("fsmonadm", "fsmonadm"); + +# Take a snapshot of all systems +verbose "Taking snapshots..."; + +foreach ($fsmondb->getSystem) { + verbose "Snapshotting $_"; + +# my ($errno, $errmsg) = $fsmondb->snapshot ($_, PROMPT); + my ($errno, $errmsg) = $fsmondb->snapshot ($_); + + warning "Unable to take snapshot of $_\n$errmsg" + if $errno != 0; +} # foreach + +verbose "Done"; diff --git a/clients/GD/FSMon/fsmon.php b/clients/GD/FSMon/fsmon.php new file mode 100644 index 0000000..e784569 --- /dev/null +++ b/clients/GD/FSMon/fsmon.php @@ -0,0 +1,233 @@ + + + + + + + FSMon v($version) $sysLabel - $mountLabel + + +END; + + $header .= banner (); + $header .= <<Filesystem Monitor +END; + + return $header; +} // createHeader + +function createPage ($system, $mount = "", $period = "daily", $scale = "gig") { + $data = getFSInfo ($system, $mount, $period); + + $page .= << +
+ + + + + + + + + + + +END; + + foreach ($data as $line) { + $page .= << + + + + + + + + +END; + } // foreach + + $page .=<< +
TimeUserTypeActionPathNameVersion + Comment
$time$user$type$action$path$element$version$comments
SystemMount PointTimestampSizeUsedFreeReserve
$line[sysname]$line[mount]$line[timestamp]$line[size]$line[used]$line[free]$line[reserve]
+END; + + return $page; +} // createPage + +function displayReport ($system = "", $mount = "", $period = "daily", $scale = "gig") { + print createPage ($system, $mount, $period, $scale); +} // displayReport + +function displayMount ($system = "", $mount = "", $period = "daily", $scale = "gig") { + global $script; + + print << + +
+ System:  + +END; + + foreach (getMounts ($system) as $item) { + print "$item"; + } // foreach + + print << +  Period:  + +END; + + foreach (getScales () as $item) { + print "$item"; + } // foreach + + print << +   + + + + + + + + +END; +// displayReport ($system, $mount, $period, $scale); +} // displayMount + +function displayFilesystems ($system = "", $mount = "", $period = "daily", $scale = "gig") { + if (empty ($mount)) { + foreach (getMounts ($system) as $mount) { + displayMount ($system, $mount, $period, $scale); + print "

"; + } // foreach + } else { + displayMount ($system, $mount, $period, $scale); + } // if +} // displayFilesystems + +function displayGraph ($system = "", $mount = "", $period = "daily", $scale = "gig") { + print createHeader (); + + if (empty ($system)) { + foreach (getSystem () as $system) { + displayFilesystems ($system["name"], $mount, $period, $scale); + } // foreach + } else { + displayFilesystems ($system, $mount, $period, $scale); + } // if +} // displayGraph + +openDB (); + +if (empty ($system)) { + print createHeader (); + print "
    "; + + foreach (getSystem () as $system) { + print "
  • $system[name]
  • "; + +// print "
      "; + +// $mounts = getMounts ($system["name"]); + +// foreach ($mounts as $mount) { +// print "
    • $mount
    • "; +// } // foreach + +// print "
    "; + } // foreach + + print "
"; +} else { + displayGraph ($system, $mount, $period, $scale); +} // if + +print copyright (); +?> + + diff --git a/clients/GD/FSMon/graphFS.php b/clients/GD/FSMon/graphFS.php new file mode 100644 index 0000000..db1b945 --- /dev/null +++ b/clients/GD/FSMon/graphFS.php @@ -0,0 +1,174 @@ + 12) { + $hours = $hours - 12; + $ampm = "Pm"; + } elseif ($hours < 10) { + $hours = substr ($hours, 1, 1); + } // if + + $Xlabel = "$hours:$minutes $ampm"; + } elseif ($period == "daily") { + $day = substr ($result["timestamp"], 8, 2); + + if ($day < 10) { + $day = substr ($day, 1, 1); + } // if + + $month = substr ($result["timestamp"], 5, 2); + + if ($month < 10) { + $month = substr ($month, 1, 1); + } // if + + $year = substr ($result["timestamp"], 0, 4); + $Xlabel = "$month/$day/$year"; + } elseif ($period == "weekly") { + $Xlabel = "Weekly not implemented"; + } elseif ($period == "monthly") { + $month = substr ($result["timestamp"], 5, 2); + + if ($month < 10) { + $month = substr ($month, 1, 1); + } // if + + $year = substr ($result["timestamp"], 0, 4); + $Xlabel = "$month/$year"; + } else { + $Xlabel = $result["timestamp"]; + } // if + + $DataSet->AddPoint ($result["used"] / $scaling, "Used", $Xlabel); + $DataSet->AddPoint ($result["free"] / $scaling, "Free", $Xlabel); +} // foreach + +$DataSet->AddAllSeries(); +$DataSet->SetAbsciseLabelSerie(); + +$DataSet->SetXAxisName ("Time"); + +// Initialise the graph +$Test = new pChart (700, 280); + +$Test->setColorPalette (1, 0, 255, 0); +$Test->setColorPalette (0, 255, 0, 0); + +$Test->drawGraphAreaGradient (100, 150, 175, 100, TARGET_BACKGROUND); +$Test->setFontProperties ("$fonts/tahoma.ttf", 8); + +if ($scaling == BYTE) { + $Test->setGraphArea (110, 30, 680, 200); + $DataSet->SetYAxisName ("Bytes"); +} elseif ($scaling == KBYTE) { + $Test->setGraphArea (90, 30, 680, 200); + $DataSet->SetYAxisName ("Kbytes"); +} elseif ($scaling == MEG) { + $Test->setGraphArea (70, 30, 680, 200); + $DataSet->SetYAxisName ("Meg"); +} else { + $Test->setGraphArea (55, 30, 680, 200); + $DataSet->SetYAxisName ("Gig"); +} // if + +$Test->drawRoundedRectangle (5, 5, 695, 275, 5, 230, 230, 230); +$Test->drawGraphAreaGradient (162, 183, 202, 50); +$Test->drawScale ($DataSet->GetData (), $DataSet->GetDataDescription (), SCALE_ADDALLSTART0, 200, 200, 200, true, 70, 2, true); +$Test->drawGrid (4, true, 230, 230, 230, 50); + +// Draw the 0 line +$Test->setFontProperties ("$fonts/tahoma.ttf", 6); +$Test->drawTreshold (0, 143, 55, 72, true, true); + +// Draw the bar graph +$Test->drawStackedBarGraph ($DataSet->GetData (), $DataSet->GetDataDescription (), 75); + +// Finish the graph +$Test->setFontProperties ("$fonts/tahoma.ttf",8); +$Test->drawLegend (610, 35, $DataSet->GetDataDescription (), 130, 180, 205); +$Test->setFontProperties ("$fonts/tahoma.ttf", 10); +$Test->drawTitle (50, 22, "$system:$mount ($period)", 255, 255, 255, 675); +$Test->Stroke (); +?> \ No newline at end of file diff --git a/clients/GD/FSMon/pChart/pCache.class b/clients/GD/FSMon/pChart/pCache.class new file mode 100644 index 0000000..454bbad --- /dev/null +++ b/clients/GD/FSMon/pChart/pCache.class @@ -0,0 +1,119 @@ +. + + Class initialisation : + pCache($CacheFolder="Cache/") + Cache management : + IsInCache($Data) + GetFromCache($ID,$Data) + WriteToCache($ID,$Data,$Picture) + DeleteFromCache($ID,$Data) + ClearCache() + Inner functions : + GetHash($ID,$Data) + */ + + /* pCache class definition */ + class pCache + { + var $HashKey = ""; + var $CacheFolder = "Cache/"; + + /* Create the pCache object */ + function pCache($CacheFolder="Cache/") + { + $this->CacheFolder = $CacheFolder; + } + + /* This function is clearing the cache folder */ + function ClearCache() + { + if ($handle = opendir($this->CacheFolder)) + { + while (false !== ($file = readdir($handle))) + { + if ( $file != "." && $file != ".." ) + unlink($this->CacheFolder.$file); + } + closedir($handle); + } + } + + /* This function is checking if we have an offline version of this chart */ + function IsInCache($ID,$Data,$Hash="") + { + if ( $Hash == "" ) + $Hash = $this->GetHash($ID,$Data); + + if ( file_exists($this->CacheFolder.$Hash) ) + return(TRUE); + else + return(FALSE); + } + + /* This function is making a copy of drawn chart in the cache folder */ + function WriteToCache($ID,$Data,$Picture) + { + $Hash = $this->GetHash($ID,$Data); + $FileName = $this->CacheFolder.$Hash; + + imagepng($Picture->Picture,$FileName); + } + + /* This function is removing any cached copy of this chart */ + function DeleteFromCache($ID,$Data) + { + $Hash = $this->GetHash($ID,$Data); + $FileName = $this->CacheFolder.$Hash; + + if ( file_exists($FileName ) ) + unlink($FileName); + } + + /* This function is retrieving the cached picture if applicable */ + function GetFromCache($ID,$Data) + { + $Hash = $this->GetHash($ID,$Data); + if ( $this->IsInCache("","",$Hash ) ) + { + $FileName = $this->CacheFolder.$Hash; + + header('Content-type: image/png'); + @readfile($FileName); + exit(); + } + } + + /* This function is building the graph unique hash key */ + function GetHash($ID,$Data) + { + $mKey = "$ID"; + foreach($Data as $key => $Values) + { + $tKey = ""; + foreach($Values as $Serie => $Value) + $tKey = $tKey.$Serie.$Value; + $mKey = $mKey.md5($tKey); + } + return(md5($mKey)); + } + } +?> \ No newline at end of file diff --git a/clients/GD/FSMon/pChart/pChart.class b/clients/GD/FSMon/pChart/pChart.class new file mode 100644 index 0000000..2c56b20 --- /dev/null +++ b/clients/GD/FSMon/pChart/pChart.class @@ -0,0 +1,2883 @@ +. + + Class initialisation : + pChart($XSize,$YSize) + Draw methods : + drawBackground($R,$G,$B) + drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B) + drawFilledRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B,$DrawBorder=TRUE,$Alpha=100) + drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B) + drawFilledRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B) + drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0) + drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0) + drawEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B) + drawFilledEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B) + drawLine($X1,$Y1,$X2,$Y2,$R,$G,$B,$GraphFunction=FALSE) + drawDottedLine($X1,$Y1,$X2,$Y2,$DotSize,$R,$G,$B) + drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B) + drawFromPNG($FileName,$X,$Y,$Alpha=100) + drawFromGIF($FileName,$X,$Y,$Alpha=100) + drawFromJPG($FileName,$X,$Y,$Alpha=100) + Graph setup methods : + addBorder($Width=3,$R=0,$G=0,$B=0) + drawGraphArea($R,$G,$B,$Stripe=FALSE) + drawScale(&$Data,&$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1) + drawGrid($LineWidth,$Mosaic=TRUE,$R=220,$G=220,$B=220,$Alpha=100) + drawLegend($XPos,$YPos,&$DataDescription,$R,$G,$B,$Rs=-1,$Gs=-1,$Bs=-1) + drawPieLegend($XPos,$YPos,$Data,$DataDescription,$R,$G,$B) + drawTitle($XPos,$YPos,$Value,$R,$G,$B,$XPos2=-1,$YPos2=-1) + drawTreshold($Value,$R,$G,$B,$ShowLabel=FALSE,$ShowOnRight=FALSE,$TickWidth=4,$FreeText=NULL) + drawArea(&$Data,$Serie1,$Serie2,$R,$G,$B,$Alpha = 50) + drawRadarAxis(&$Data,&$DataDescription,$Mosaic=TRUE,$BorderOffset=10,$A_R=60,$A_G=60,$A_B=60,$S_R=200,$S_G=200,$S_B=200,$MaxValue=-1) + drawGraphAreaGradient($R,$G,$B,$Decay,$Target=TARGET_GRAPHAREA) + drawTextBox($X1,$Y1,$X2,$Y2,$Text,$Angle=0,$R=255,$G=255,$B=255,$Align=ALIGN_LEFT,$Shadow=TRUE,$BgR=-1,$BgG=-1,$BgB=-1,$Alpha=100) + getLegendBoxSize($DataDescription) + loadColorPalette($FileName,$Delimiter=",") + reportWarnings($Interface="CLI") + setGraphArea($X1,$Y1,$X2,$Y2) + setFixedScale($VMin,$VMax) + setLabel(&$Data,&$DataDescription,$SerieName,$ValueName,$Caption,$R=210,$G=210,$B=210) + setColorPalette($ID,$R,$G,$B) + setDateFormat($Format) + setFontProperties($FontName,$FontSize) + setLineStyle($Width=1,$DotSize=0) + setFixedScale($VMin,$VMax,$Divisions=5) + writeValues(&$Data,&$DataDescription,$Series) + Graphs methods : + drawPlotGraph(&$Data,&$DataDescription,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1) + drawLineGraph(&$Data,&$DataDescription,$SerieName="") + drawFilledLineGraph(&$Data,&$DataDescription,$Alpha=100,$AroundZero=FALSE) + drawCubicCurve(&$Data,&$DataDescription,$Accuracy=.1,$SerieName="") + drawFilledCubicCurve(&$Data,&$DataDescription,$Accuracy=.1,$Alpha=100,$AroundZero=FALSE) + drawOverlayBarGraph(&$Data,&$DataDescription,$Alpha=50) + drawBarGraph(&$Data,&$DataDescription,$Shadow=FALSE) + drawStackedBarGraph(&$Data,&$DataDescription,$Alpha=50) + drawLimitsGraph(&$Data,&$DataDescription,$R=0,$G=0,$B=0) + drawRadar(&$Data,&$DataDescription,$BorderOffset=10,$MaxValue=-1) + drawFilledRadar(&$Data,&$DataDescription,$Alpha=50,$BorderOffset=10,$MaxValue=-1) + drawBasicPieGraph(&$Data,&$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$R=255,$G=255,$B=255,$Decimals=0) + drawFlatPieGraph(&$Data,&$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals = 0) + drawPieGraph(&$Data,&$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$EnhanceColors=TRUE,$Skew=60,$SpliceHeight=20,$SpliceDistance=0,$Decimals=0) + Other methods : + setImageMap($Mode=TRUE,$GraphID="MyGraph") + getImageMap($MapName,$Flush=TRUE) + Render($FileName) + Stroke() + */ + + /* Andrew@DeFaria.com: Empty array for imageftbbox call + See http://bugs.php.net/bug.php?id=26309 + */ + /* Declare some script wide constants */ + define("SCALE_NORMAL",1); + define("SCALE_ADDALL",2); + define("SCALE_START0",3); + define("SCALE_ADDALLSTART0",4); + define("PIE_PERCENTAGE", 1); + define("PIE_LABELS",2); + define("PIE_NOLABEL",3); + define("TARGET_GRAPHAREA",1); + define("TARGET_BACKGROUND",2); + define("ALIGN_TOP_LEFT",1); + define("ALIGN_TOP_CENTER",2); + define("ALIGN_TOP_RIGHT",3); + define("ALIGN_LEFT",4); + define("ALIGN_CENTER",5); + define("ALIGN_RIGHT",6); + define("ALIGN_BOTTOM_LEFT",7); + define("ALIGN_BOTTOM_CENTER",8); + define("ALIGN_BOTTOM_RIGHT",9); + + /* pChart class definition */ + class pChart + { + /* Palettes definition */ + var $Palette = array("0"=>array("R"=>188,"G"=>224,"B"=>46), + "1"=>array("R"=>224,"G"=>100,"B"=>46), + "2"=>array("R"=>224,"G"=>214,"B"=>46), + "3"=>array("R"=>46,"G"=>151,"B"=>224), + "4"=>array("R"=>176,"G"=>46,"B"=>224), + "5"=>array("R"=>224,"G"=>46,"B"=>117), + "6"=>array("R"=>92,"G"=>224,"B"=>46), + "7"=>array("R"=>224,"G"=>176,"B"=>46)); + + /* Some static vars used in the class */ + var $XSize = NULL; + var $YSize = NULL; + var $Picture = NULL; + var $ImageMap = NULL; + + /* Error management */ + var $ErrorReporting = FALSE; + var $ErrorInterface = "CLI"; + var $Errors = NULL; + var $ErrorFontName = "Fonts/pf_arma_five.ttf"; + var $ErrorFontSize = 6; + + /* vars related to the graphing area */ + var $GArea_X1 = NULL; + var $GArea_Y1 = NULL; + var $GArea_X2 = NULL; + var $GArea_Y2 = NULL; + var $GAreaXOffset = NULL; + var $VMax = NULL; + var $VMin = NULL; + var $Divisions = NULL; + var $DivisionHeight = NULL; + var $DivisionCount = NULL; + var $DivisionRatio = NULL; + var $DivisionWidth = NULL; + var $DataCount = NULL; + + /* Text format related vars */ + var $FontName = NULL; + var $FontSize = NULL; + var $DateFormat = "d/m/Y"; + + /* Lines format related vars */ + var $LineWidth = 1; + var $LineDotSize = 0; + + /* Layer related vars */ + var $Layers = NULL; + + /* Set antialias quality : 0 is maximum, 100 minimum*/ + var $AntialiasQuality = 10; + + /* Image Map settings */ + var $BuildMap = FALSE; + var $MapFunction = NULL; + var $tmpFolder = "tmp/"; + var $MapID = NULL; + + /* This function create the background picture */ + function pChart($XSize,$YSize) + { + $this->XSize = $XSize; + $this->YSize = $YSize; + $this->Picture = imagecreatetruecolor($XSize,$YSize); + + $C_White = imagecolorallocate($this->Picture,255,255,255); + imagefilledrectangle($this->Picture,0,0,$XSize,$YSize,$C_White); + imagecolortransparent($this->Picture,$C_White); + + $this->setFontProperties("tahoma.ttf",8); + } + + /* Set if warnings should be reported */ + function reportWarnings($Interface="CLI") + { + $this->ErrorReporting = TRUE; + $this->ErrorInterface = $Interface; + } + + /* Set the font properties */ + function setFontProperties($FontName,$FontSize) + { + $this->FontName = $FontName; + $this->FontSize = $FontSize; + } + + /* Set Palette color */ + function setColorPalette($ID,$R,$G,$B) + { + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + $this->Palette[$ID]["R"] = $R; + $this->Palette[$ID]["G"] = $G; + $this->Palette[$ID]["B"] = $B; + } + + /* Load Color Palette from file */ + function loadColorPalette($FileName,$Delimiter=",") + { + $handle = @fopen($FileName,"r"); + $ColorID = 0; + if ($handle) + { + while (!feof($handle)) + { + $buffer = fgets($handle, 4096); + $buffer = str_replace(chr(10),"",$buffer); + $buffer = str_replace(chr(13),"",$buffer); + $Values = split($Delimiter,$buffer); + if ( count($Values) == 3 ) + { + $this->Palette[$ColorID]["R"] = $Values[0]; + $this->Palette[$ColorID]["G"] = $Values[1]; + $this->Palette[$ColorID]["B"] = $Values[2]; + $ColorID++; + } + } + } + } + + /* Set line style */ + function setLineStyle($Width=1,$DotSize=0) + { + $this->LineWidth = $Width; + $this->LineDotSize = $DotSize; + } + + /* Set the graph area location */ + function setGraphArea($X1,$Y1,$X2,$Y2) + { + $this->GArea_X1 = $X1; + $this->GArea_Y1 = $Y1; + $this->GArea_X2 = $X2; + $this->GArea_Y2 = $Y2; + } + + /* Prepare the graph area */ + function drawGraphArea($R,$G,$B,$Stripe=FALSE) + { + $this->drawFilledRectangle($this->GArea_X1,$this->GArea_Y1,$this->GArea_X2,$this->GArea_Y2,$R,$G,$B,FALSE); + $this->drawRectangle($this->GArea_X1,$this->GArea_Y1,$this->GArea_X2,$this->GArea_Y2,$R-40,$G-40,$B-40); + + if ( $Stripe ) + { + $R2 = $R-15; if ( $R2 < 0 ) { $R2 = 0; } + $G2 = $R-15; if ( $G2 < 0 ) { $G2 = 0; } + $B2 = $R-15; if ( $B2 < 0 ) { $B2 = 0; } + + $LineColor = imagecolorallocate($this->Picture,$R2,$G2,$B2); + $SkewWidth = $this->GArea_Y2-$this->GArea_Y1-1; + + for($i=$this->GArea_X1-$SkewWidth;$i<=$this->GArea_X2;$i=$i+4) + { + $X1 = $i; $Y1 = $this->GArea_Y2; + $X2 = $i+$SkewWidth; $Y2 = $this->GArea_Y1; + + + if ( $X1 < $this->GArea_X1 ) + { $X1 = $this->GArea_X1; $Y1 = $this->GArea_Y1 + $X2 - $this->GArea_X1 + 1; } + + if ( $X2 >= $this->GArea_X2 ) + { $Y2 = $this->GArea_Y1 + $X2 - $this->GArea_X2 +1; $X2 = $this->GArea_X2 - 1; } +// * Fixed in 1.27 * { $X2 = $this->GArea_X2 - 1; $Y2 = $this->GArea_Y2 - ($this->GArea_X2 - $X1); } + + imageline($this->Picture,$X1,$Y1,$X2,$Y2+1,$LineColor); + } + } + } + + /* Allow you to fix the scale, use this to bypass the automatic scaling */ + function setFixedScale($VMin,$VMax,$Divisions=5) + { + $this->VMin = $VMin; + $this->VMax = $VMax; + $this->Divisions = $Divisions; + } + + /* Compute and draw the scale */ + function drawScale(&$Data,&$DataDescription,$ScaleMode,$R,$G,$B,$DrawTicks=TRUE,$Angle=0,$Decimals=1,$WithMargin=FALSE,$SkipLabels=1) + { + /* Validate the Data and DataDescription array */ + $this->validateData("drawScale",$Data); + + $C_TextColor = imagecolorallocate($this->Picture,$R,$G,$B); + + $this->drawLine($this->GArea_X1,$this->GArea_Y1,$this->GArea_X1,$this->GArea_Y2,$R,$G,$B); + $this->drawLine($this->GArea_X1,$this->GArea_Y2,$this->GArea_X2,$this->GArea_Y2,$R,$G,$B); + + if ( $this->VMin == NULL && $this->VMax == NULL) + { + if (isset($DataDescription["Values"][0])) + { + $this->VMin = $Data[0][$DataDescription["Values"][0]]; + $this->VMax = $Data[0][$DataDescription["Values"][0]]; + } + else { $this->VMin = 2147483647; $this->VMax = -2147483647; } + + /* Compute Min and Max values */ + if ( $ScaleMode == SCALE_NORMAL || $ScaleMode == SCALE_START0 ) + { + if ( $ScaleMode == SCALE_START0 ) { $this->VMin = 0; } + + foreach ( $Data as $Key => $Values ) + { + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + if (isset($Data[$Key][$ColName])) + { + $Value = $Data[$Key][$ColName]; + + if ( is_numeric($Value) ) + { + if ( $this->VMax < $Value) { $this->VMax = $Value; } + if ( $this->VMin > $Value) { $this->VMin = $Value; } + } + } + } + } + } + elseif ( $ScaleMode == SCALE_ADDALL || $ScaleMode == SCALE_ADDALLSTART0 ) /* Experimental */ + { + if ( $ScaleMode == SCALE_ADDALLSTART0 ) { $this->VMin = 0; } + + foreach ( $Data as $Key => $Values ) + { + $Sum = 0; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + if (isset($Data[$Key][$ColName])) + { + $Value = $Data[$Key][$ColName]; + if ( is_numeric($Value) ) + $Sum += $Value; + } + } + if ( $this->VMax < $Sum) { $this->VMax = $Sum; } + if ( $this->VMin > $Sum) { $this->VMin = $Sum; } + } + } + + $DataRange = $this->VMax - $this->VMin; + if ( $DataRange == 0 ) { $DataRange = .1; } + + /* Compute automatic scaling */ + $ScaleOk = FALSE; $Factor = 1; + $MinDivHeight = 25; $MaxDivs = ($this->GArea_Y2 - $this->GArea_Y1) / $MinDivHeight; + if ($MaxDivs > 1) + { + while(!$ScaleOk) + { + $Scale1 = ( $this->VMax - $this->VMin ) / $Factor; + $Scale2 = ( $this->VMax - $this->VMin ) / $Factor / 2; + $Scale4 = ( $this->VMax - $this->VMin ) / $Factor / 4; + + if ( $Scale1 > 1 && $Scale1 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale1); $Scale = 1;} + if ( $Scale2 > 1 && $Scale2 <= $MaxDivs && !$ScaleOk) { $ScaleOk = TRUE; $Divisions = floor($Scale2); $Scale = 2;} + if (!$ScaleOk) + { + if ( $Scale2 > 1 ) { $Factor = $Factor * 10; } + if ( $Scale2 < 1 ) { $Factor = $Factor / 10; } + } + } + + if ( floor($this->VMax / $Scale / $Factor) != $this->VMax / $Scale / $Factor) + { + $GridID = floor ( $this->VMax / $Scale / $Factor) + 1; + $this->VMax = $GridID * $Scale * $Factor; + $Divisions++; + } + + if ( floor($this->VMin / $Scale / $Factor) != $this->VMin / $Scale / $Factor) + { + $GridID = floor( $this->VMin / $Scale / $Factor); + $this->VMin = $GridID * $Scale * $Factor; + $Divisions++; + } + } + else /* Can occurs for small graphs */ + $Scale = 1; + + if ( !isset($Divisions) ) + $Divisions = 2; + + if ($Scale == 1 && $Divisions%2 == 1) + $Divisions--; + } + else + $Divisions = $this->Divisions; + + $this->DivisionCount = $Divisions; + + $DataRange = $this->VMax - $this->VMin; + if ( $DataRange == 0 ) { $DataRange = .1; } + + $this->DivisionHeight = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $Divisions; + $this->DivisionRatio = ( $this->GArea_Y2 - $this->GArea_Y1 ) / $DataRange; + + $this->GAreaXOffset = 0; + if ( count($Data) > 1 ) + { + if ( $WithMargin == FALSE ) + $this->DivisionWidth = ( $this->GArea_X2 - $this->GArea_X1 ) / (count($Data)-1); + else + { + $this->DivisionWidth = ( $this->GArea_X2 - $this->GArea_X1 ) / (count($Data)); + $this->GAreaXOffset = $this->DivisionWidth / 2; + } + } + else + { + $this->DivisionWidth = $this->GArea_X2 - $this->GArea_X1; + $this->GAreaXOffset = $this->DivisionWidth / 2; + } + + $this->DataCount = count($Data); + + if ( $DrawTicks == FALSE ) + return(0); + + $YPos = $this->GArea_Y2; $XMin = NULL; + for($i=1;$i<=$Divisions+1;$i++) + { + $this->drawLine($this->GArea_X1,$YPos,$this->GArea_X1-5,$YPos,$R,$G,$B); + $Value = $this->VMin + ($i-1) * (( $this->VMax - $this->VMin ) / $Divisions); + $Value = round($Value * pow(10,$Decimals)) / pow(10,$Decimals); + if ( $DataDescription["Format"]["Y"] == "number" ) + $Value = $Value.$DataDescription["Unit"]["Y"]; + if ( $DataDescription["Format"]["Y"] == "time" ) + $Value = $this->ToTime($Value); + if ( $DataDescription["Format"]["Y"] == "date" ) + $Value = $this->ToDate($Value); + if ( $DataDescription["Format"]["Y"] == "metric" ) + $Value = $this->ToMetric($Value); + + $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value,$bogus); + $TextWidth = $Position[2]-$Position[0]; + imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1-10-$TextWidth,$YPos+($this->FontSize/2),$C_TextColor,$this->FontName,$Value); + + if ( $XMin > $this->GArea_X1-10-$TextWidth || $XMin == NULL ) { $XMin = $this->GArea_X1-10-$TextWidth; } + + $YPos = $YPos - $this->DivisionHeight; + } + + /* Write the Y Axis caption if set */ + if ( isset($DataDescription["Axis"]["Y"]) ) + { + $Position = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["Y"],$bogus); + $TextHeight = abs($Position[1])+abs($Position[3]); + $TextTop = (($this->GArea_Y2 - $this->GArea_Y1) / 2) + $this->GArea_Y1 + ($TextHeight/2); + imagettftext($this->Picture,$this->FontSize,90,$XMin-$this->FontSize,$TextTop,$C_TextColor,$this->FontName,$DataDescription["Axis"]["Y"]); + } + + /* Horizontal Axis */ + $XPos = $this->GArea_X1 + $this->GAreaXOffset; + $ID = 1; $YMax = NULL; + foreach ( $Data as $Key => $Values ) + { + if ( $ID % $SkipLabels == 0 ) + { + $this->drawLine(floor($XPos),$this->GArea_Y2,floor($XPos),$this->GArea_Y2+5,$R,$G,$B); + $Value = $Data[$Key][$DataDescription["Position"]]; + if ( $DataDescription["Format"]["X"] == "number" ) + $Value = $Value.$DataDescription["Unit"]["X"]; + if ( $DataDescription["Format"]["X"] == "time" ) + $Value = $this->ToTime($Value); + if ( $DataDescription["Format"]["X"] == "date" ) + $Value = $this->ToDate($Value); + if ( $DataDescription["Format"]["X"] == "metric" ) + $Value = $this->ToMetric($Value); + + $Position = imageftbbox($this->FontSize,$Angle,$this->FontName,$Value,$bogus); + $TextWidth = abs($Position[2])+abs($Position[0]); + $TextHeight = abs($Position[1])+abs($Position[3]); + + if ( $Angle == 0 ) + { + $YPos = $this->GArea_Y2+18; + imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-floor($TextWidth/2),$YPos,$C_TextColor,$this->FontName,$Value); + } + else + { + $YPos = $this->GArea_Y2+10+$TextHeight; + if ( $Angle <= 90 ) + imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)-$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value); + else + imagettftext($this->Picture,$this->FontSize,$Angle,floor($XPos)+$TextWidth+5,$YPos,$C_TextColor,$this->FontName,$Value); + } + if ( $YMax < $YPos || $YMax == NULL ) { $YMax = $YPos; } + } + + $XPos = $XPos + $this->DivisionWidth; + $ID++; + } + + /* Write the X Axis caption if set */ + if ( isset($DataDescription["Axis"]["X"]) ) + { + $Position = imageftbbox($this->FontSize,90,$this->FontName,$DataDescription["Axis"]["X"],$bogus); + $TextWidth = abs($Position[2])+abs($Position[0]); + $TextLeft = (($this->GArea_X2 - $this->GArea_X1) / 2) + $this->GArea_X1 + ($TextWidth/2); + imagettftext($this->Picture,$this->FontSize,0,$TextLeft,$YMax+$this->FontSize+5,$C_TextColor,$this->FontName,$DataDescription["Axis"]["X"]); + } + } + + /* Compute and draw the scale */ + function drawGrid($LineWidth,$Mosaic=TRUE,$R=220,$G=220,$B=220,$Alpha=100) + { + /* Draw mosaic */ + if ( $Mosaic ) + { + $LayerWidth = $this->GArea_X2-$this->GArea_X1; + $LayerHeight = $this->GArea_Y2-$this->GArea_Y1; + + $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight); + $C_White = imagecolorallocate($this->Layers[0],255,255,255); + imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White); + imagecolortransparent($this->Layers[0],$C_White); + + $C_Rectangle = imagecolorallocate($this->Layers[0],250,250,250); + + $YPos = $LayerHeight; //$this->GArea_Y2-1; + $LastY = $YPos; + for($i=0;$i<=$this->DivisionCount;$i++) + { + $LastY = $YPos; + $YPos = $YPos - $this->DivisionHeight; + + if ( $YPos <= 0 ) { $YPos = 1; } + + if ( $i % 2 == 0 ) + { + imagefilledrectangle($this->Layers[0],1,$YPos,$LayerWidth-1,$LastY,$C_Rectangle); + } + } + imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha); + imagedestroy($this->Layers[0]); + } + + /* Horizontal lines */ + $YPos = $this->GArea_Y2 - $this->DivisionHeight; + for($i=1;$i<=$this->DivisionCount;$i++) + { + if ( $YPos > $this->GArea_Y1 && $YPos < $this->GArea_Y2 ) + $this->drawDottedLine($this->GArea_X1,$YPos,$this->GArea_X2,$YPos,$LineWidth,$R,$G,$B); + + $YPos = $YPos - $this->DivisionHeight; + } + + /* Vertical lines */ + if ( $this->GAreaXOffset == 0 ) + { $XPos = $this->GArea_X1 + $this->DivisionWidth + $this->GAreaXOffset; $ColCount = $this->DataCount-2; } + else + { $XPos = $this->GArea_X1 + $this->GAreaXOffset; $ColCount = $this->DataCount; } + + for($i=1;$i<=$ColCount;$i++) + { + if ( $XPos > $this->GArea_X1 && $XPos < $this->GArea_X2 ) + $this->drawDottedLine(floor($XPos),$this->GArea_Y1,floor($XPos),$this->GArea_Y2,$LineWidth,$R,$G,$B); + $XPos = $XPos + $this->DivisionWidth; + } + } + + /* retrieve the legends size */ + function getLegendBoxSize($DataDescription) + { + if ( !isset($DataDescription["Description"]) ) + return(-1); + + /* <-10->[8]<-4->Text<-10-> */ + $MaxWidth = 0; $MaxHeight = 8; + foreach($DataDescription["Description"] as $Key => $Value) + { + $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value,$bogus); + $TextWidth = $Position[2]-$Position[0]; + $TextHeight = $Position[1]-$Position[7]; + if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; } + $MaxHeight = $MaxHeight + $TextHeight + 4; + } + $MaxHeight = $MaxHeight - 3; + $MaxWidth = $MaxWidth + 32; + + return(array($MaxWidth,$MaxHeight)); + } + + /* Draw the data legends */ + function drawLegend($XPos,$YPos,&$DataDescription,$R,$G,$B,$Rs=-1,$Gs=-1,$Bs=-1) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawLegend",$DataDescription); + + if ( !isset($DataDescription["Description"]) ) + return(-1); + + $C_TextColor = imagecolorallocate($this->Picture,0,0,0); + + /* <-10->[8]<-4->Text<-10-> */ + $MaxWidth = 0; $MaxHeight = 8; + foreach($DataDescription["Description"] as $Key => $Value) + { + $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value,$bogus); + $TextWidth = $Position[2]-$Position[0]; + $TextHeight = $Position[1]-$Position[7]; + if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; } + $MaxHeight = $MaxHeight + $TextHeight + 4; + } + $MaxHeight = $MaxHeight - 5; + $MaxWidth = $MaxWidth + 32; + + if ( $Rs == -1 || $Gs == -1 || $Bs == -1 ) + { $Rs = $R-30; $Gs = $G-30; $Bs = $B-30; } + + $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$Rs,$Gs,$Bs); + $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B); + + $YOffset = 4 + $this->FontSize; $ID = 0; + foreach($DataDescription["Description"] as $Key => $Value) + { + $this->drawFilledRoundedRectangle($XPos+10,$YPos+$YOffset-4,$XPos+14,$YPos+$YOffset-4,2,$this->Palette[$ID]["R"],$this->Palette[$ID]["G"],$this->Palette[$ID]["B"]); + imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value); + + $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value,$bogus); + $TextHeight = $Position[1]-$Position[7]; + + $YOffset = $YOffset + $TextHeight + 4; + $ID++; + } + } + + /* Draw the data legends */ + function drawPieLegend($XPos,$YPos,&$Data,&$DataDescription,$R,$G,$B) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawPieLegend",$DataDescription,FALSE); + $this->validateData("drawPieLegend",$Data); + + if ( !isset($DataDescription["Position"]) ) + return(-1); + + $C_TextColor = imagecolorallocate($this->Picture,0,0,0); + + /* <-10->[8]<-4->Text<-10-> */ + $MaxWidth = 0; $MaxHeight = 8; + foreach($Data as $Key => $Value) + { + $Value2 = $Value[$DataDescription["Position"]]; + $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value2,$bogus); + $TextWidth = $Position[2]-$Position[0]; + $TextHeight = $Position[1]-$Position[7]; + if ( $TextWidth > $MaxWidth) { $MaxWidth = $TextWidth; } + + $MaxHeight = $MaxHeight + $TextHeight + 4; + } + $MaxHeight = $MaxHeight - 3; + $MaxWidth = $MaxWidth + 32; + + $this->drawFilledRoundedRectangle($XPos+1,$YPos+1,$XPos+$MaxWidth+1,$YPos+$MaxHeight+1,5,$R-30,$G-30,$B-30); + $this->drawFilledRoundedRectangle($XPos,$YPos,$XPos+$MaxWidth,$YPos+$MaxHeight,5,$R,$G,$B); + + $YOffset = 4 + $this->FontSize; $ID = 0; + foreach($Data as $Key => $Value) + { + $Value2 = $Value[$DataDescription["Position"]]; + $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value2,$bogus); + $TextHeight = $Position[1]-$Position[7]; + $this->drawFilledRectangle($XPos+10,$YPos+$YOffset-6,$XPos+14,$YPos+$YOffset-2,$this->Palette[$ID]["R"],$this->Palette[$ID]["G"],$this->Palette[$ID]["B"]); + + imagettftext($this->Picture,$this->FontSize,0,$XPos+22,$YPos+$YOffset,$C_TextColor,$this->FontName,$Value2); + $YOffset = $YOffset + $TextHeight + 4; + $ID++; + } + } + + /* Draw the graph title */ + function drawTitle($XPos,$YPos,$Value,$R,$G,$B,$XPos2 = -1, $YPos2 = -1) + { + $C_TextColor = imagecolorallocate($this->Picture,$R,$G,$B); + + if ( $XPos2 != -1 ) + { + $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value,$bogus); + $TextWidth = $Position[2]-$Position[0]; + $XPos = floor(( $XPos2 - $XPos - $TextWidth ) / 2 ) + $XPos; + } + + if ( $YPos2 != -1 ) + { + $Position = imageftbbox($this->FontSize,0,$this->FontName,$Value,$bogus); + $TextHeight = $Position[5]-$Position[3]; + $YPos = floor(( $YPos2 - $YPos - $TextHeight ) / 2 ) + $YPos; + } + + imagettftext($this->Picture,$this->FontSize,0,$XPos,$YPos,$C_TextColor,$this->FontName,$Value); + } + + /* Draw a text box with text align & alpha properties */ + function drawTextBox($X1,$Y1,$X2,$Y2,$Text,$Angle=0,$R=255,$G=255,$B=255,$Align=ALIGN_LEFT,$Shadow=TRUE,$BgR=-1,$BgG=-1,$BgB=-1,$Alpha=100) + { + $Position = imageftbbox($this->FontSize,$Angle,$this->FontName,$Text,$bogus); + $TextWidth = $Position[2]-$Position[0]; + $TextHeight = $Position[5]-$Position[3]; + $AreaWidth = $X2 - $X1; + $AreaHeight = $Y2 - $Y1; + + if ( $BgR != -1 && $BgG != -1 && $BgB != -1 ) + $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$BgR,$BgG,$BgB,FALSE,$Alpha); + + if ( $Align == ALIGN_TOP_LEFT ) { $X = $X1+1; $Y = $Y1+$this->FontSize+1; } + if ( $Align == ALIGN_TOP_CENTER ) { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y1+$this->FontSize+1; } + if ( $Align == ALIGN_TOP_RIGHT ) { $X = $X2-$TextWidth-1; $Y = $Y1+$this->FontSize+1; } + if ( $Align == ALIGN_LEFT ) { $X = $X1+1; $Y = $Y1+($AreaHeight/2)-($TextHeight/2); } + if ( $Align == ALIGN_CENTER ) { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y1+($AreaHeight/2)-($TextHeight/2); } + if ( $Align == ALIGN_RIGHT ) { $X = $X2-$TextWidth-1; $Y = $Y1+($AreaHeight/2)-($TextHeight/2); } + if ( $Align == ALIGN_BOTTOM_LEFT ) { $X = $X1+1; $Y = $Y2-1; } + if ( $Align == ALIGN_BOTTOM_CENTER ) { $X = $X1+($AreaWidth/2)-($TextWidth/2); $Y = $Y2-1; } + if ( $Align == ALIGN_BOTTOM_RIGHT ) { $X = $X2-$TextWidth-1; $Y = $Y2-1; } + + $C_TextColor = imagecolorallocate($this->Picture,$R,$G,$B); + $C_ShadowColor = imagecolorallocate($this->Picture,0,0,0); + if ( $Shadow ) + imagettftext($this->Picture,$this->FontSize,$Angle,$X+1,$Y+1,$C_ShadowColor,$this->FontName,$Text); + + imagettftext($this->Picture,$this->FontSize,$Angle,$X,$Y,$C_TextColor,$this->FontName,$Text); + } + + /* Compute and draw the scale */ + function drawTreshold($Value,$R,$G,$B,$ShowLabel=FALSE,$ShowOnRight=FALSE,$TickWidth=4,$FreeText=NULL) + { + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + $C_TextColor = imagecolorallocate($this->Picture,$R,$G,$B); + $Y = $this->GArea_Y2 - ($Value - $this->VMin) * $this->DivisionRatio; + + if ( $Y <= $this->GArea_Y1 || $Y >= $this->GArea_Y2 ) + return(-1); + + if ( $TickWidth == 0 ) + $this->drawLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$R,$G,$B); + else + $this->drawDottedLine($this->GArea_X1,$Y,$this->GArea_X2,$Y,$TickWidth,$R,$G,$B); + + if ( $ShowLabel ) + { + if ( $FreeText == NULL ) + { $Label = $Value; } else { $Label = $FreeText; } + + if ( $ShowOnRight ) + imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X2+2,$Y+($this->FontSize/2),$C_TextColor,$this->FontName,$Label); + else + imagettftext($this->Picture,$this->FontSize,0,$this->GArea_X1+2,$Y-($this->FontSize/2),$C_TextColor,$this->FontName,$Label); + } + } + + /* This function put a label on a specific point */ + function setLabel(&$Data,&$DataDescription,$SerieName,$ValueName,$Caption,$R=210,$G=210,$B=210) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("setLabel",$DataDescription); + $this->validateData("setLabel",$Data); + + $C_Label = imagecolorallocate($this->Picture,$R,$G,$B); + $C_Shadow = imagecolorallocate($this->Picture,$R-30,$G-30,$B-30); + $C_TextColor = imagecolorallocate($this->Picture,0,0,0); + + $Cp = 0; $Found = FALSE; + foreach ( $Data as $Key => $Value ) + { + if ( $Data[$Key][$DataDescription["Position"]] == $ValueName ) + { $NumericalValue = $Data[$Key][$SerieName]; $Found = TRUE; } + if ( !$Found ) + $Cp++; + } + + $XPos = $this->GArea_X1 + $this->GAreaXOffset + ( $this->DivisionWidth * $Cp ) + 2; + $YPos = $this->GArea_Y2 - ($NumericalValue - $this->VMin) * $this->DivisionRatio; + + $Position = imageftbbox($this->FontSize,0,$this->FontName,$Caption,$bogus); + $TextHeight = $Position[3] - $Position[5]; + $TextWidth = $Position[2]-$Position[0]; + $TextOffset = floor($TextHeight/2); + + // Shadow + $Poly = array($XPos+1,$YPos+1,$XPos + 9,$YPos - $TextOffset,$XPos + 8,$YPos + $TextOffset + 2); + imagefilledpolygon($this->Picture,$Poly,3,$C_Shadow); + $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos - $TextOffset - 1,$R-30,$G-30,$B-30); + $this->drawLine($XPos,$YPos+1,$XPos + 9,$YPos + $TextOffset + 3,$R-30,$G-30,$B-30); + $this->drawFilledRectangle($XPos + 9,$YPos - $TextOffset,$XPos + 13 + $TextWidth,$YPos + $TextOffset + 2,$R-30,$G-30,$B-30); + + // Label background + $Poly = array($XPos,$YPos,$XPos + 8,$YPos - $TextOffset - 1,$XPos + 8,$YPos + $TextOffset + 1); + imagefilledpolygon($this->Picture,$Poly,3,$C_Label); + $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos - $TextOffset - 2,$R,$G,$B); + $this->drawLine($XPos-1,$YPos,$XPos + 8,$YPos + $TextOffset + 2,$R,$G,$B); + $this->drawFilledRectangle($XPos + 8,$YPos - $TextOffset - 1,$XPos + 12 + $TextWidth,$YPos + $TextOffset + 1,$R,$G,$B); + + imagettftext($this->Picture,$this->FontSize,0,$XPos + 10,$YPos + $TextOffset,$C_TextColor,$this->FontName,$Caption); + } + + /* This function draw a line graph */ + function drawPlotGraph(&$Data,&$DataDescription,$BigRadius=5,$SmallRadius=2,$R2=-1,$G2=-1,$B2=-1) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawPlotGraph",$DataDescription); + $this->validateData("drawPlotGraph",$Data); + + $GraphID = 0; + + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + $ID = 0; + foreach ( $DataDescription["Description"] as $keyI => $ValueI ) + { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; } + + $R = $this->Palette[$ColorID]["R"]; + $G = $this->Palette[$ColorID]["G"]; + $B = $this->Palette[$ColorID]["B"]; + + if ( isset($DataDescription["Symbol"][$ColName]) ) + { + //$Is_Alpha = ((ord ( file_get_contents ($DataDescription["Symbol"][$ColName], false, null, 25, 1)) & 6) & 4) == 4; + $Is_Alpha = ((ord ( file_get_contents ($DataDescription["Symbol"][$ColName], false)) & 6) & 4) == 4; + + $Infos = getimagesize($DataDescription["Symbol"][$ColName]); + $ImageWidth = $Infos[0]; + $ImageHeight = $Infos[1]; + $Symbol = imagecreatefromgif($DataDescription["Symbol"][$ColName]); + } + + $XPos = $this->GArea_X1 + $this->GAreaXOffset; + $Hsize = round($BigRadius/2); + foreach ( $Data as $Key => $Values ) + { + $Value = $Data[$Key][$ColName]; + $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio); + + /* Save point into the image map if option activated */ + if ( $this->BuildMap ) + $this->addToImageMap($XPos-$Hsize,$YPos-$Hsize,$XPos+1+$Hsize,$YPos+$Hsize+1,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Plot"); + + if ( is_numeric($Value) ) + { + if ( !isset($DataDescription["Symbol"][$ColName]) ) + { + $this->drawFilledCircle($XPos+1,$YPos+1,$BigRadius,$R,$G,$B); + + if ( $R2 !=-1 && $G2 !=-1 && $B2 !=-1 ) + $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R2,$G2,$B2); + else + { + $R = $this->Palette[$ColorID]["R"]-5; if ( $R < 0 ) { $R = 0; } + $G = $this->Palette[$ColorID]["G"]-5; if ( $G < 0 ) { $G = 0; } + $B = $this->Palette[$ColorID]["B"]-5; if ( $B < 0 ) { $B = 0; } + + $this->drawFilledCircle($XPos+1,$YPos+1,$SmallRadius,$R,$G,$B); + } + } + else + { + imagecopymerge($this->Picture,$Symbol,$XPos+1-$ImageWidth/2,$YPos+1-$ImageHeight/2,0,0,$ImageWidth,$ImageHeight,100); + } + } + + $XPos = $XPos + $this->DivisionWidth; + } + $GraphID++; + } + } + + + /* This function draw an area between two series */ + function drawArea(&$Data,$Serie1,$Serie2,$R,$G,$B,$Alpha = 50) + { + /* Validate the Data and DataDescription array */ + $this->validateData("drawArea",$Data); + + $LayerWidth = $this->GArea_X2-$this->GArea_X1; + $LayerHeight = $this->GArea_Y2-$this->GArea_Y1; + + $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight); + $C_White = imagecolorallocate($this->Layers[0],255,255,255); + imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White); + imagecolortransparent($this->Layers[0],$C_White); + + $C_Graph = imagecolorallocate($this->Layers[0],$R,$G,$B); + + $XPos = $this->GAreaXOffset; + $LastXPos = -1; + foreach ( $Data as $Key => $Values ) + { + $Value1 = $Data[$Key][$Serie1]; + $Value2 = $Data[$Key][$Serie2]; + $YPos1 = $LayerHeight - (($Value1-$this->VMin) * $this->DivisionRatio); + $YPos2 = $LayerHeight - (($Value2-$this->VMin) * $this->DivisionRatio); + + if ( $LastXPos != -1 ) + { + $Points = ""; + $Points[] = $LastXPos; $Points[] = $LastYPos1; + $Points[] = $LastXPos; $Points[] = $LastYPos2; + $Points[] = $XPos; $Points[] = $YPos2; + $Points[] = $XPos; $Points[] = $YPos1; + + imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph); + } + + $LastYPos1 = $YPos1; + $LastYPos2 = $YPos2; + $LastXPos = $XPos; + + $XPos = $XPos + $this->DivisionWidth; + } + + imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha); + imagedestroy($this->Layers[0]); + } + + + /* This function write the values of the specified series */ + function writeValues(&$Data,&$DataDescription,$Series) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("writeValues",$DataDescription); + $this->validateData("writeValues",$Data); + + if ( !is_array($Series) ) { $Series = array($Series); } + + foreach($Series as $Key => $Serie) + { + $ID = 0; + foreach ( $DataDescription["Description"] as $keyI => $ValueI ) + { if ( $keyI == $Serie ) { $ColorID = $ID; }; $ID++; } + + $XPos = $this->GArea_X1 + $this->GAreaXOffset; + $XLast = -1; + foreach ( $Data as $Key => $Values ) + { + if ( isset($Data[$Key][$Serie]) && is_numeric($Data[$Key][$Serie])) + { + $Value = $Data[$Key][$Serie]; + $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio); + + $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Value); + $Width = $Positions[2] - $Positions[6]; $XOffset = $XPos - ($Width/2); + $Height = $Positions[3] - $Positions[7]; $YOffset = $YPos - 4; + + $C_TextColor = imagecolorallocate($this->Picture,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]); + imagettftext($this->Picture,$this->FontSize,0,$XOffset,$YOffset,$C_TextColor,$this->FontName,$Value); + } + $XPos = $XPos + $this->DivisionWidth; + } + + } + } + + /* This function draw a line graph */ + function drawLineGraph(&$Data,&$DataDescription,$SerieName="") + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawLineGraph",$DataDescription); + $this->validateData("drawLineGraph",$Data); + + $GraphID = 0; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + $ID = 0; + foreach ( $DataDescription["Description"] as $keyI => $ValueI ) + { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; } + + if ( $SerieName == "" || $SerieName == $ColName ) + { + $XPos = $this->GArea_X1 + $this->GAreaXOffset; + $XLast = -1; + foreach ( $Data as $Key => $Values ) + { + if ( isset($Data[$Key][$ColName])) + { + $Value = $Data[$Key][$ColName]; + $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio); + + /* Save point into the image map if option activated */ + if ( $this->BuildMap ) + $this->addToImageMap($XPos-3,$YPos-3,$XPos+3,$YPos+3,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Line"); + + if (!is_numeric($Value)) { $XLast = -1; } + if ( $XLast != -1 ) + $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE); + + $XLast = $XPos; + $YLast = $YPos; + if (!is_numeric($Value)) { $XLast = -1; } + } + $XPos = $XPos + $this->DivisionWidth; + } + $GraphID++; + } + } + } + + /* This function draw a cubic curve */ + function drawCubicCurve(&$Data,&$DataDescription,$Accuracy=.1,$SerieName="") + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawCubicCurve",$DataDescription); + $this->validateData("drawCubicCurve",$Data); + + $GraphID = 0; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + if ( $SerieName == "" || $SerieName == $ColName ) + { + $XIn = ""; $Yin = ""; $Yt = ""; $U = ""; + $XIn[0] = 0; $YIn[0] = 0; + + $ID = 0; + foreach ( $DataDescription["Description"] as $keyI => $ValueI ) + { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; } + + $Index = 1; + $XLast = -1; $Missing = ""; + foreach ( $Data as $Key => $Values ) + { + if ( isset($Data[$Key][$ColName]) ) + { + $Value = $Data[$Key][$ColName]; + $XIn[$Index] = $Index; + $YIn[$Index] = $Value; + if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; } + $Index++; + } + } + $Index--; + + $Yt[0] = 0; + $Yt[1] = 0; + $U[1] = 0; + for($i=2;$i<=$Index-1;$i++) + { + $Sig = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]); + $p = $Sig * $Yt[$i-1] + 2; + $Yt[$i] = ($Sig - 1) / $p; + $U[$i] = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]); + $U[$i] = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p; + } + + $qn = 0; + $un = 0; + $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1); + + for($k=$Index-1;$k>=1;$k--) + $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k]; + + $XPos = $this->GArea_X1 + $this->GAreaXOffset; + for($X=1;$X<=$Index;$X=$X+$Accuracy) + { + $klo = 1; + $khi = $Index; + $k = $khi - $klo; + while($k > 1) + { + $k = $khi - $klo; + If ( $XIn[$k] >= $X ) + $khi = $k; + else + $klo = $k; + } + $klo = $khi - 1; + + $h = $XIn[$khi] - $XIn[$klo]; + $a = ($XIn[$khi] - $X) / $h; + $b = ($X - $XIn[$klo]) / $h; + $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6; + + $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio); + + if ( $XLast != -1 && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)]) ) + $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE); + + $XLast = $XPos; + $YLast = $YPos; + $XPos = $XPos + $this->DivisionWidth * $Accuracy; + } + + // Add potentialy missing values + $XPos = $XPos - $this->DivisionWidth * $Accuracy; + if ( $XPos < ($this->GArea_X2 - $this->GAreaXOffset) ) + { + $YPos = $this->GArea_Y2 - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio); + $this->drawLine($XLast,$YLast,$this->GArea_X2-$this->GAreaXOffset,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE); + } + + $GraphID++; + } + } + } + + /* This function draw a filled cubic curve */ + function drawFilledCubicCurve(&$Data,&$DataDescription,$Accuracy=.1,$Alpha=100,$AroundZero=FALSE) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawFilledCubicCurve",$DataDescription); + $this->validateData("drawFilledCubicCurve",$Data); + + $LayerWidth = $this->GArea_X2-$this->GArea_X1; + $LayerHeight = $this->GArea_Y2-$this->GArea_Y1; + $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio); + if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; } + + $GraphID = 0; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + $XIn = ""; $Yin = ""; $Yt = ""; $U = ""; + $XIn[0] = 0; $YIn[0] = 0; + + $ID = 0; + foreach ( $DataDescription["Description"] as $keyI => $ValueI ) + { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; } + + $Index = 1; + $XLast = -1; $Missing = ""; + foreach ( $Data as $Key => $Values ) + { + $Value = $Data[$Key][$ColName]; + $XIn[$Index] = $Index; + $YIn[$Index] = $Value; + if ( !is_numeric($Value) ) { $Missing[$Index] = TRUE; } + $Index++; + } + $Index--; + + $Yt[0] = 0; + $Yt[1] = 0; + $U[1] = 0; + for($i=2;$i<=$Index-1;$i++) + { + $Sig = ($XIn[$i] - $XIn[$i-1]) / ($XIn[$i+1] - $XIn[$i-1]); + $p = $Sig * $Yt[$i-1] + 2; + $Yt[$i] = ($Sig - 1) / $p; + $U[$i] = ($YIn[$i+1] - $YIn[$i]) / ($XIn[$i+1] - $XIn[$i]) - ($YIn[$i] - $YIn[$i-1]) / ($XIn[$i] - $XIn[$i-1]); + $U[$i] = (6 * $U[$i] / ($XIn[$i+1] - $XIn[$i-1]) - $Sig * $U[$i-1]) / $p; + } + + $qn = 0; + $un = 0; + $Yt[$Index] = ($un - $qn * $U[$Index-1]) / ($qn * $Yt[$Index-1] + 1); + + for($k=$Index-1;$k>=1;$k--) + $Yt[$k] = $Yt[$k] * $Yt[$k+1] + $U[$k]; + + $Points = ""; + $Points[] = $this->GAreaXOffset; + $Points[] = $LayerHeight; + + $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight); + $C_White = imagecolorallocate($this->Layers[0],255,255,255); + imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White); + imagecolortransparent($this->Layers[0],$C_White); + + $YLast = NULL; + $XPos = $this->GAreaXOffset; $PointsCount = 2; + for($X=1;$X<=$Index;$X=$X+$Accuracy) + { + $klo = 1; + $khi = $Index; + $k = $khi - $klo; + while($k > 1) + { + $k = $khi - $klo; + If ( $XIn[$k] >= $X ) + $khi = $k; + else + $klo = $k; + } + $klo = $khi - 1; + + $h = $XIn[$khi] - $XIn[$klo]; + $a = ($XIn[$khi] - $X) / $h; + $b = ($X - $XIn[$klo]) / $h; + $Value = $a * $YIn[$klo] + $b * $YIn[$khi] + (($a*$a*$a - $a) * $Yt[$klo] + ($b*$b*$b - $b) * $Yt[$khi]) * ($h*$h) / 6; + + $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio); + + if ( $YLast != NULL && $AroundZero && !isset($Missing[floor($X)]) && !isset($Missing[floor($X+1)])) + { + $aPoints = ""; + $aPoints[] = $XLast; + $aPoints[] = $YLast; + $aPoints[] = $XPos; + $aPoints[] = $YPos; + $aPoints[] = $XPos; + $aPoints[] = $YZero; + $aPoints[] = $XLast; + $aPoints[] = $YZero; + + $C_Graph = imagecolorallocate($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]); + imagefilledpolygon($this->Layers[0],$aPoints,4,$C_Graph); + } + + if ( !isset($Missing[floor($X)]) || $YLast == NULL ) + { + $PointsCount++; + $Points[] = $XPos; + $Points[] = $YPos; + } + else + { + $PointsCount++; $Points[] = $XLast; $Points[] = $LayerHeight; + } + + $YLast = $YPos; $XLast = $XPos; + $XPos = $XPos + $this->DivisionWidth * $Accuracy; + } + + // Add potentialy missing values + $XPos = $XPos - $this->DivisionWidth * $Accuracy; + if ( $XPos < ($LayerWidth-$this->GAreaXOffset) ) + { + $YPos = $LayerHeight - (($YIn[$Index]-$this->VMin) * $this->DivisionRatio); + + if ( $YLast != NULL && $AroundZero ) + { + $aPoints = ""; + $aPoints[] = $XLast; + $aPoints[] = $YLast; + $aPoints[] = $LayerWidth-$this->GAreaXOffset; + $aPoints[] = $YPos; + $aPoints[] = $LayerWidth-$this->GAreaXOffset; + $aPoints[] = $YZero; + $aPoints[] = $XLast; + $aPoints[] = $YZero; + + $C_Graph = imagecolorallocate($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]); + imagefilledpolygon($this->Layers[0],$aPoints,4,$C_Graph); + } + + if ( $YIn[$klo] != "" && $YIn[$khi] != "" || $YLast == NULL ) + { + $PointsCount++; + $Points[] = $LayerWidth-$this->GAreaXOffset; + $Points[] = $YPos; + } + } + + $Points[] = $LayerWidth-$this->GAreaXOffset; + $Points[] = $LayerHeight; + + if ( !$AroundZero ) + { + $C_Graph = imagecolorallocate($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]); + imagefilledpolygon($this->Layers[0],$Points,$PointsCount,$C_Graph); + } + + imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha); + imagedestroy($this->Layers[0]); + + $this->drawCubicCurve($Data,$DataDescription,$Accuracy,$ColName); + + $GraphID++; + } + } + + /* This function draw a filled line graph */ + function drawFilledLineGraph(&$Data,&$DataDescription,$Alpha=100,$AroundZero=FALSE) + { + $Empty = -2147483647; + + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawFilledLineGraph",$DataDescription); + $this->validateData("drawFilledLineGraph",$Data); + + $LayerWidth = $this->GArea_X2-$this->GArea_X1; + $LayerHeight = $this->GArea_Y2-$this->GArea_Y1; + + $GraphID = 0; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + $ID = 0; + foreach ( $DataDescription["Description"] as $keyI => $ValueI ) + { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; } + + $aPoints = ""; + $aPoints[] = $this->GAreaXOffset; + $aPoints[] = $LayerHeight; + + $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight); + $C_White = imagecolorallocate($this->Layers[0],255,255,255); + imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White); + imagecolortransparent($this->Layers[0],$C_White); + + $XPos = $this->GAreaXOffset; + $XLast = -1; $PointsCount = 2; + $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio); + if ( $YZero > $LayerHeight ) { $YZero = $LayerHeight; } + + $YLast = $Empty; + foreach ( $Data as $Key => $Values ) + { + $Value = $Data[$Key][$ColName]; + $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio); + + /* Save point into the image map if option activated */ + if ( $this->BuildMap ) + $this->addToImageMap($XPos-3,$YPos-3,$XPos+3,$YPos+3,$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"FLine"); + + if ( !is_numeric($Value) ) + { + $PointsCount++; + $aPoints[] = $XLast; + $aPoints[] = $LayerHeight; + + $YLast = $Empty; + } + else + { + $PointsCount++; + if ( $YLast <> $Empty ) + { $aPoints[] = $XPos; $aPoints[] = $YPos; } + else + { $PointsCount++; $aPoints[] = $XPos; $aPoints[] = $LayerHeight; $aPoints[] = $XPos; $aPoints[] = $YPos; } + + if ($YLast <> $Empty && $AroundZero) + { + $Points = ""; + $Points[] = $XLast; $Points[] = $YLast; + $Points[] = $XPos; + $Points[] = $YPos; + $Points[] = $XPos; + $Points[] = $YZero; + $Points[] = $XLast; + $Points[] = $YZero; + + $C_Graph = imagecolorallocate($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]); + imagefilledpolygon($this->Layers[0],$Points,4,$C_Graph); + } + $YLast = $YPos; + } + + $XLast = $XPos; + $XPos = $XPos + $this->DivisionWidth; + } + $aPoints[] = $LayerWidth - $this->GAreaXOffset; + $aPoints[] = $LayerHeight; + + if ( $AroundZero == FALSE ) + { + $C_Graph = imagecolorallocate($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]); + imagefilledpolygon($this->Layers[0],$aPoints,$PointsCount,$C_Graph); + } + + imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha); + imagedestroy($this->Layers[0]); + $GraphID++; + $this->drawLineGraph($Data,$DataDescription,$ColName); + } + } + + /* This function draw a bar graph */ + function drawOverlayBarGraph(&$Data,&$DataDescription,$Alpha=50) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawOverlayBarGraph",$DataDescription); + $this->validateData("drawOverlayBarGraph",$Data); + + $LayerWidth = $this->GArea_X2-$this->GArea_X1; + $LayerHeight = $this->GArea_Y2-$this->GArea_Y1; + + $GraphID = 0; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + $ID = 0; + foreach ( $DataDescription["Description"] as $keyI => $ValueI ) + { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; } + + $this->Layers[$GraphID] = imagecreatetruecolor($LayerWidth,$LayerHeight); + $C_White = imagecolorallocate($this->Layers[$GraphID],255,255,255); + $C_Graph = imagecolorallocate($this->Layers[$GraphID],$this->Palette[$GraphID]["R"],$this->Palette[$GraphID]["G"],$this->Palette[$GraphID]["B"]); + imagefilledrectangle($this->Layers[$GraphID],0,0,$LayerWidth,$LayerHeight,$C_White); + imagecolortransparent($this->Layers[$GraphID],$C_White); + + $XWidth = $this->DivisionWidth / 4; + $XPos = $this->GAreaXOffset; + $YZero = $LayerHeight - ((0-$this->VMin) * $this->DivisionRatio); + $XLast = -1; $PointsCount = 2; + foreach ( $Data as $Key => $Values ) + { + if ( isset($Data[$Key][$ColName]) ) + { + $Value = $Data[$Key][$ColName]; + if ( is_numeric($Value) ) + { + $YPos = $LayerHeight - (($Value-$this->VMin) * $this->DivisionRatio); + + imagefilledrectangle($this->Layers[$GraphID],$XPos-$XWidth,$YPos,$XPos+$XWidth,$YZero,$C_Graph); + + $X1 = floor($XPos - $XWidth + $this->GArea_X1); $Y1 = floor($YPos+$this->GArea_Y1) + .2; + $X2 = floor($XPos + $XWidth + $this->GArea_X1); $Y2 = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio); + if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; } + if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; } + + /* Save point into the image map if option activated */ + if ( $this->BuildMap ) + $this->addToImageMap($X1,min($Y1,$Y2),$X2,max($Y1,$Y2),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"oBar"); + + $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE); + } + } + $XPos = $XPos + $this->DivisionWidth; + } + + $GraphID++; + } + + for($i=0;$i<=($GraphID-1);$i++) + { + imagecopymerge($this->Picture,$this->Layers[$i],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha); + imagedestroy($this->Layers[$i]); + } + } + + /* This function draw a bar graph */ + function drawBarGraph(&$Data,&$DataDescription,$Shadow=FALSE,$Alpha=100) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawBarGraph",$DataDescription); + $this->validateData("drawBarGraph",$Data); + + $GraphID = 0; + $Series = count($DataDescription["Values"]); + $SeriesWidth = $this->DivisionWidth / ($Series+1); + $SerieXOffset = $this->DivisionWidth / 2 - $SeriesWidth / 2; + + $YZero = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio); + if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; } + + $SerieID = 0; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + $ID = 0; + foreach ( $DataDescription["Description"] as $keyI => $ValueI ) + { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; } + + $XPos = $this->GArea_X1 + $this->GAreaXOffset - $SerieXOffset + $SeriesWidth * $SerieID; + $XLast = -1; + foreach ( $Data as $Key => $Values ) + { + if ( isset($Data[$Key][$ColName])) + { + if ( is_numeric($Data[$Key][$ColName]) ) + { + $Value = $Data[$Key][$ColName]; + $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio); + + /* Save point into the image map if option activated */ + if ( $this->BuildMap ) + { + $this->addToImageMap($XPos+1,min($YZero,$YPos),$XPos+$SeriesWidth-1,max($YZero,$YPos),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"Bar"); + } + + if ( $Shadow && $Alpha == 100 ) + $this->drawRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,25,25,25,TRUE,$Alpha); + + $this->drawFilledRectangle($XPos+1,$YZero,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha); + } + } + $XPos = $XPos + $this->DivisionWidth; + } + $SerieID++; + } + } + + /* This function draw a stacked bar graph */ + function drawStackedBarGraph(&$Data,&$DataDescription,$Alpha=50) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawBarGraph",$DataDescription); + $this->validateData("drawBarGraph",$Data); + + $GraphID = 0; + $Series = count($DataDescription["Values"]); + $SeriesWidth = $this->DivisionWidth * .8; + + $YZero = $this->GArea_Y2 - ((0-$this->VMin) * $this->DivisionRatio); + if ( $YZero > $this->GArea_Y2 ) { $YZero = $this->GArea_Y2; } + + $SerieID = 0; $LastValue = ""; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + $ID = 0; + foreach ( $DataDescription["Description"] as $keyI => $ValueI ) + { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; } + + $XPos = $this->GArea_X1 + $this->GAreaXOffset - $SeriesWidth / 2; + $XLast = -1; + foreach ( $Data as $Key => $Values ) + { + if ( isset($Data[$Key][$ColName])) + { + if ( is_numeric($Data[$Key][$ColName]) ) + { + $Value = $Data[$Key][$ColName]; + + if ( isset($LastValue[$Key]) ) + { + $YPos = $this->GArea_Y2 - ((($Value+$LastValue[$Key])-$this->VMin) * $this->DivisionRatio); + $YBottom = $this->GArea_Y2 - (($LastValue[$Key]-$this->VMin) * $this->DivisionRatio); + $LastValue[$Key] += $Value; + } + else + { + $YPos = $this->GArea_Y2 - (($Value-$this->VMin) * $this->DivisionRatio); + $YBottom = $YZero; + $LastValue[$Key] = $Value; + } + + /* Save point into the image map if option activated */ + if ( $this->BuildMap ) + $this->addToImageMap($XPos+1,min($YBottom,$YPos),$XPos+$SeriesWidth-1,max($YBottom,$YPos),$DataDescription["Description"][$ColName],$Data[$Key][$ColName].$DataDescription["Unit"]["Y"],"sBar"); + + $this->drawFilledRectangle($XPos+1,$YBottom,$XPos+$SeriesWidth-1,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"],TRUE,$Alpha); + } + } + $XPos = $XPos + $this->DivisionWidth; + } + $SerieID++; + } + } + + /* This function draw a limits bar graphs */ + function drawLimitsGraph(&$Data,&$DataDescription,$R=0,$G=0,$B=0) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawLimitsGraph",$DataDescription); + $this->validateData("drawLimitsGraph",$Data); + + $XWidth = $this->DivisionWidth / 4; + $XPos = $this->GArea_X1 + $this->GAreaXOffset; + + foreach ( $Data as $Key => $Values ) + { + $Min = $Data[$Key][$DataDescription["Values"][0]]; + $Max = $Data[$Key][$DataDescription["Values"][0]]; + $GraphID = 0; $MaxID = 0; $MinID = 0; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + if ( isset($Data[$Key][$ColName]) ) + { + if ( $Data[$Key][$ColName] > $Max && is_numeric($Data[$Key][$ColName])) + { $Max = $Data[$Key][$ColName]; $MaxID = $GraphID; } + } + if ( isset($Data[$Key][$ColName]) && is_numeric($Data[$Key][$ColName])) + { + if ( $Data[$Key][$ColName] < $Min ) + { $Min = $Data[$Key][$ColName]; $MinID = $GraphID; } + $GraphID++; + } + } + + $YPos = $this->GArea_Y2 - (($Max-$this->VMin) * $this->DivisionRatio); + $X1 = floor($XPos - $XWidth); $Y1 = floor($YPos) - .2; + $X2 = floor($XPos + $XWidth); + if ( $X1 <= $this->GArea_X1 ) { $X1 = $this->GArea_X1 + 1; } + if ( $X2 >= $this->GArea_X2 ) { $X2 = $this->GArea_X2 - 1; } + + $YPos = $this->GArea_Y2 - (($Min-$this->VMin) * $this->DivisionRatio); + $Y2 = floor($YPos) + .2; + + $this->drawLine(floor($XPos)-.2,$Y1+1,floor($XPos)-.2,$Y2-1,$R,$G,$B,TRUE); + $this->drawLine(floor($XPos)+.2,$Y1+1,floor($XPos)+.2,$Y2-1,$R,$G,$B,TRUE); + $this->drawLine($X1,$Y1,$X2,$Y1,$this->Palette[$MaxID]["R"],$this->Palette[$MaxID]["G"],$this->Palette[$MaxID]["B"],FALSE); + $this->drawLine($X1,$Y2,$X2,$Y2,$this->Palette[$MinID]["R"],$this->Palette[$MinID]["G"],$this->Palette[$MinID]["B"],FALSE); + + $XPos = $XPos + $this->DivisionWidth; + } + } + + /* This function draw radar axis centered on the graph area */ + function drawRadarAxis(&$Data,&$DataDescription,$Mosaic=TRUE,$BorderOffset=10,$A_R=60,$A_G=60,$A_B=60,$S_R=200,$S_G=200,$S_B=200,$MaxValue=-1) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawRadarAxis",$DataDescription); + $this->validateData("drawRadarAxis",$Data); + + $C_TextColor = imagecolorallocate($this->Picture,$A_R,$A_G,$A_B); + + /* Draw radar axis */ + $Points = count($Data); + $Radius = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset; + $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1; + $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1; + + /* Search for the max value */ + if ( $MaxValue == -1 ) + { + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + foreach ( $Data as $Key => $Values ) + { + if ( isset($Data[$Key][$ColName])) + if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; } + } + } + } + + /* Draw the mosaic */ + if ( $Mosaic ) + { + $RadiusScale = $Radius / $MaxValue; + for ( $t=1; $t<=$MaxValue-1; $t++) + { + $TRadius = $RadiusScale * $t; + $LastX1 = -1; + + for ( $i=0; $i<=$Points; $i++) + { + $Angle = -90 + $i * 360/$Points; + $X1 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter; + $Y1 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter; + $X2 = cos($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $XCenter; + $Y2 = sin($Angle * 3.1418 / 180 ) * ($TRadius+$RadiusScale) + $YCenter; + + if ( $t % 2 == 1 && $LastX1 != -1) + { + $Plots = ""; + $Plots[] = $X1; $Plots[] = $Y1; + $Plots[] = $X2; $Plots[] = $Y2; + $Plots[] = $LastX2; $Plots[] = $LastY2; + $Plots[] = $LastX1; $Plots[] = $LastY1; + + $C_Graph = imagecolorallocate($this->Picture,250,250,250); + imagefilledpolygon($this->Picture,$Plots,(count($Plots)+1)/2,$C_Graph); + } + + $LastX1 = $X1; $LastY1= $Y1; + $LastX2 = $X2; $LastY2= $Y2; + } + } + } + + + /* Draw the spider web */ + for ( $t=1; $t<=$MaxValue; $t++) + { + $TRadius = ( $Radius / $MaxValue ) * $t; + $LastX = -1; + + for ( $i=0; $i<=$Points; $i++) + { + $Angle = -90 + $i * 360/$Points; + $X = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter; + $Y = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter; + + if ( $LastX != -1 ) + $this->drawDottedLine($LastX,$LastY,$X,$Y,4,$S_R,$S_G,$S_B); + + $LastX = $X; $LastY= $Y; + } + } + + /* Draw the axis */ + for ( $i=0; $i<=$Points; $i++) + { + $Angle = -90 + $i * 360/$Points; + $X = cos($Angle * 3.1418 / 180 ) * $Radius + $XCenter; + $Y = sin($Angle * 3.1418 / 180 ) * $Radius + $YCenter; + + $this->drawLine($XCenter,$YCenter,$X,$Y,$A_R,$A_G,$A_B); + + $XOffset = 0; $YOffset = 0; + if (isset($Data[$i][$DataDescription["Position"]])) + { + $Label = $Data[$i][$DataDescription["Position"]]; + + $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$Label); + $Width = $Positions[2] - $Positions[6]; + $Height = $Positions[3] - $Positions[7]; + + if ( $Angle >= 0 && $Angle <= 90 ) + $YOffset = $Height; + + if ( $Angle > 90 && $Angle <= 180 ) + { $YOffset = $Height; $XOffset = -$Width; } + + if ( $Angle > 180 && $Angle <= 270 ) + { $XOffset = -$Width; } + + imagettftext($this->Picture,$this->FontSize,0,$X+$XOffset,$Y+$YOffset,$C_TextColor,$this->FontName,$Label); + } + } + + /* Write the values */ + for ( $t=1; $t<=$MaxValue; $t++) + { + $TRadius = ( $Radius / $MaxValue ) * $t; + + $Angle = -90 + 360 / $Points; + $X1 = $XCenter; + $Y1 = $YCenter - $TRadius; + $X2 = cos($Angle * 3.1418 / 180 ) * $TRadius + $XCenter; + $Y2 = sin($Angle * 3.1418 / 180 ) * $TRadius + $YCenter; + + $XPos = floor(($X2-$X1)/2) + $X1; + $YPos = floor(($Y2-$Y1)/2) + $Y1; + + $Positions = imagettfbbox($this->FontSize,0,$this->FontName,$t); + $X = $XPos - ( $X+$Positions[2] - $X+$Positions[6] ) / 2; + $Y = $YPos + $this->FontSize; + + $this->drawFilledRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,240,240,240); + $this->drawRoundedRectangle($X+$Positions[6]-2,$Y+$Positions[7]-1,$X+$Positions[2]+4,$Y+$Positions[3]+1,2,220,220,220); + imagettftext($this->Picture,$this->FontSize,0,$X,$Y,$C_TextColor,$this->FontName,$t); + } + } + + /* This function draw a radar graph centered on the graph area */ + function drawRadar(&$Data,&$DataDescription,$BorderOffset=10,$MaxValue=-1) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawRadar",$DataDescription); + $this->validateData("drawRadar",$Data); + + $Points = count($Data); + $Radius = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset; + $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2 + $this->GArea_X1; + $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 + $this->GArea_Y1; + + /* Search for the max value */ + if ( $MaxValue == -1 ) + { + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + foreach ( $Data as $Key => $Values ) + { + if ( isset($Data[$Key][$ColName])) + if ( $Data[$Key][$ColName] > $MaxValue ) { $MaxValue = $Data[$Key][$ColName]; } + } + } + } + + $GraphID = 0; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + $ID = 0; + foreach ( $DataDescription["Description"] as $keyI => $ValueI ) + { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; } + + $Angle = -90; + $XLast = -1; + foreach ( $Data as $Key => $Values ) + { + if ( isset($Data[$Key][$ColName])) + { + $Value = $Data[$Key][$ColName]; + $Strength = ( $Radius / $MaxValue ) * $Value; + + $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter; + $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter; + + if ( $XLast != -1 ) + $this->drawLine($XLast,$YLast,$XPos,$YPos,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]); + + if ( $XLast == -1 ) + { $FirstX = $XPos; $FirstY = $YPos; } + + $Angle = $Angle + (360/$Points); + $XLast = $XPos; + $YLast = $YPos; + } + } + $this->drawLine($XPos,$YPos,$FirstX,$FirstY,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]); + $GraphID++; + } + } + + /* This function draw a radar graph centered on the graph area */ + function drawFilledRadar(&$Data,&$DataDescription,$Alpha=50,$BorderOffset=10,$MaxValue=-1) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawFilledRadar",$DataDescription); + $this->validateData("drawFilledRadar",$Data); + + $Points = count($Data); + $LayerWidth = $this->GArea_X2-$this->GArea_X1; + $LayerHeight = $this->GArea_Y2-$this->GArea_Y1; + $Radius = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2 - $BorderOffset; + $XCenter = ( $this->GArea_X2 - $this->GArea_X1 ) / 2; + $YCenter = ( $this->GArea_Y2 - $this->GArea_Y1 ) / 2; + + /* Search for the max value */ + if ( $MaxValue == -1 ) + { + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + foreach ( $Data as $Key => $Values ) + { + if ( isset($Data[$Key][$ColName])) + if ( $Data[$Key][$ColName] > $MaxValue && is_numeric($Data[$Key][$ColName])) { $MaxValue = $Data[$Key][$ColName]; } + } + } + } + + $GraphID = 0; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + $ID = 0; + foreach ( $DataDescription["Description"] as $keyI => $ValueI ) + { if ( $keyI == $ColName ) { $ColorID = $ID; }; $ID++; } + + $Angle = -90; + $XLast = -1; + $Plots = ""; + foreach ( $Data as $Key => $Values ) + { + if ( isset($Data[$Key][$ColName])) + { + $Value = $Data[$Key][$ColName]; + if ( !is_numeric($Value) ) { $Value = 0; } + $Strength = ( $Radius / $MaxValue ) * $Value; + + $XPos = cos($Angle * 3.1418 / 180 ) * $Strength + $XCenter; + $YPos = sin($Angle * 3.1418 / 180 ) * $Strength + $YCenter; + + $Plots[] = $XPos; + $Plots[] = $YPos; + + $Angle = $Angle + (360/$Points); + $XLast = $XPos; + $YLast = $YPos; + } + } + + if (isset($Plots[0])) + { + $Plots[] = $Plots[0]; + $Plots[] = $Plots[1]; + + $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight); + $C_White = imagecolorallocate($this->Layers[0],255,255,255); + imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White); + imagecolortransparent($this->Layers[0],$C_White); + + $C_Graph = imagecolorallocate($this->Layers[0],$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]); + imagefilledpolygon($this->Layers[0],$Plots,(count($Plots)+1)/2,$C_Graph); + + imagecopymerge($this->Picture,$this->Layers[0],$this->GArea_X1,$this->GArea_Y1,0,0,$LayerWidth,$LayerHeight,$Alpha); + imagedestroy($this->Layers[0]); + + for($i=0;$i<=count($Plots)-4;$i=$i+2) + $this->drawLine($Plots[$i]+$this->GArea_X1,$Plots[$i+1]+$this->GArea_Y1,$Plots[$i+2]+$this->GArea_X1,$Plots[$i+3]+$this->GArea_Y1,$this->Palette[$ColorID]["R"],$this->Palette[$ColorID]["G"],$this->Palette[$ColorID]["B"]); + } + + $GraphID++; + } + } + + /* This function draw a flat pie chart */ + function drawBasicPieGraph(&$Data,&$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$R=255,$G=255,$B=255,$Decimals=0) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawBasicPieGraph",$DataDescription,FALSE); + $this->validateData("drawBasicPieGraph",$Data); + + /* Determine pie sum */ + $Series = 0; $PieSum = 0; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + if ( $ColName != $DataDescription["Position"] ) + { + $Series++; + foreach ( $Data as $Key => $Values ) + { + if ( isset($Data[$Key][$ColName])) + $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]]; + } + } + } + + /* Validate serie */ + if ( $Series != 1 ) + RaiseFatal("Pie chart can only accept one serie of data."); + + $SpliceRatio = 360 / $PieSum; + $SplicePercent = 100 / $PieSum; + + /* Calculate all polygons */ + $Angle = 0; $TopPlots = ""; + foreach($iValues as $Key => $Value) + { + $TopPlots[$Key][] = $XPos; + $TopPlots[$Key][] = $YPos; + + /* Process labels position & size */ + if ( !($DrawLabels == PIE_NOLABEL) ) + { + $TAngle = $Angle+($Value*$SpliceRatio/2); + if ($DrawLabels == PIE_PERCENTAGE) + $Caption = (round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%"; + elseif ($DrawLabels == PIE_LABELS) + $Caption = $iLabels[$Key]; + $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius + 10)+ $XPos; + $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+ 10) + $YPos + 4; + + if ( $TAngle > 90 && $TAngle < 270 ) + { + $Position = imageftbbox($this->FontSize,0,$this->FontName,$Caption,$bogus); + $TextWidth = $Position[2]-$Position[0]; + $TX = $TX - $TextWidth; + } + + $C_TextColor = imagecolorallocate($this->Picture,70,70,70); + imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption); + } + + /* Process pie slices */ + for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5) + { + $TopX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos; + $TopY = sin($iAngle * 3.1418 / 180 ) * $Radius + $YPos; + + $TopPlots[$Key][] = $TopX; + $TopPlots[$Key][] = $TopY; + } + + $TopPlots[$Key][] = $XPos; + $TopPlots[$Key][] = $YPos; + + $Angle = $iAngle; + } + $PolyPlots = $TopPlots; + + /* Set array values type to float --- PHP Bug with imagefilledpolygon casting to integer */ + foreach ($TopPlots as $Key => $Value) + { foreach ($TopPlots[$Key] as $Key2 => $Value2) { settype($TopPlots[$Key][$Key2],"float"); } } + + /* Draw Top polygons */ + foreach ($PolyPlots as $Key => $Value) + { + $C_GraphLo = imagecolorallocate($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]); + imagefilledpolygon($this->Picture,$PolyPlots[$Key],(count($PolyPlots[$Key])+1)/2,$C_GraphLo); + } + + $this->drawCircle($XPos-.5,$YPos-.5,$Radius,$R,$G,$B); + $this->drawCircle($XPos-.5,$YPos-.5,$Radius+.5,$R,$G,$B); + + /* Draw Top polygons */ + foreach ($TopPlots as $Key => $Value) + { + for($j=0;$j<=count($TopPlots[$Key])-4;$j=$j+2) + $this->drawLine($TopPlots[$Key][$j],$TopPlots[$Key][$j+1],$TopPlots[$Key][$j+2],$TopPlots[$Key][$j+3],$R,$G,$B); + } + } + + /* This function draw a flat pie chart */ + function drawFlatPieGraph(&$Data,&$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$SpliceDistance=0,$Decimals = 0) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawFlatPieGraph",$DataDescription,FALSE); + $this->validateData("drawFlatPieGraph",$Data); + + /* Determine pie sum */ + $Series = 0; $PieSum = 0; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + if ( $ColName != $DataDescription["Position"] ) + { + $Series++; + foreach ( $Data as $Key => $Values ) + { + if ( isset($Data[$Key][$ColName])) + $PieSum = $PieSum + $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]]; + } + } + } + + /* Validate serie */ + if ( $Series != 1 ) + RaiseFatal("Pie chart can only accept one serie of data."); + + $SpliceDistanceRatio = $SpliceDistance; + $SpliceRatio = (360 - $SpliceDistanceRatio * count($iValues) ) / $PieSum; + $SplicePercent = 100 / $PieSum; + + /* Calculate all polygons */ + $Angle = 0; $TopPlots = ""; + foreach($iValues as $Key => $Value) + { + $XCenterPos = cos(($Angle+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $XPos; + $YCenterPos = sin(($Angle+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $YPos; + + $TopPlots[$Key][] = $XCenterPos; + $TopPlots[$Key][] = $YCenterPos; + + /* Process labels position & size */ + if ( !($DrawLabels == PIE_NOLABEL) ) + { + $TAngle = $Angle+($Value*$SpliceRatio/2); + if ($DrawLabels == PIE_PERCENTAGE) + $Caption = (round($Value * pow(10,$Decimals) * $SplicePercent)/pow(10,$Decimals))."%"; + elseif ($DrawLabels == PIE_LABELS) + $Caption = $iLabels[$Key]; + $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius+10+$SpliceDistance)+$XPos; + $TY = sin(($TAngle) * 3.1418 / 180 ) * ($Radius+10+$SpliceDistance) + $YPos + 4; + + if ( $TAngle > 90 && $TAngle < 270 ) + { + $Position = imageftbbox($this->FontSize,0,$this->FontName,$Caption,$bogus); + $TextWidth = $Position[2]-$Position[0]; + $TX = $TX - $TextWidth; + } + + $C_TextColor = imagecolorallocate($this->Picture,70,70,70); + imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption); + } + + /* Draw borders to correct imagefilledpolygon bug */ + $BMax = 2; + for($i=-1;$i<=$BMax;$i++) + { + $BorderX1 = cos(($Angle+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * ($SpliceDistance+$i) + $XPos; + $BorderY1 = sin(($Angle+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * ($SpliceDistance+$i) + $YPos; + $BorderX2 = cos(($Angle+$i*.5) * 3.1418 / 180 ) * (($Radius+$BMax)+$SpliceDistance) + $XPos; + $BorderY2 = sin(($Angle+$i*.5) * 3.1418 / 180 ) * (($Radius+$BMax)+$SpliceDistance) + $YPos; + $this->drawLine($BorderX1,$BorderY1,$BorderX2,$BorderY2,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]); + + $BorderX1 = cos(($Angle+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * ($SpliceDistance+$i) + $XPos; + $BorderY1 = sin(($Angle+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * ($SpliceDistance+$i) + $YPos; + $BorderX2 = cos(($Angle-$i*.5+$Value*$SpliceRatio) * 3.1418 / 180 ) * (($Radius+$BMax)+$SpliceDistance) + $XPos; + $BorderY2 = sin(($Angle-$i*.5+$Value*$SpliceRatio) * 3.1418 / 180 ) * (($Radius+$BMax)+$SpliceDistance) + $YPos; + $this->drawLine($BorderX1,$BorderY1,$BorderX2,$BorderY2,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]); + } + + /* Process pie slices */ + for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5) + { + $TopX = cos($iAngle * 3.1418 / 180 ) * ($Radius+$SpliceDistance) + $XPos; + $TopY = sin($iAngle * 3.1418 / 180 ) * ($Radius+$SpliceDistance) + $YPos; + + $TopPlots[$Key][] = $TopX; + $TopPlots[$Key][] = $TopY; + + if ( $iAngle != $Angle ) + { + for($i=-1;$i<=2;$i++) + { + $BorderX1 = cos(($iAngle-.5) * 3.1418 / 180 ) * (($Radius+$i)+$SpliceDistance) + $XPos; + $BorderY1 = sin(($iAngle-.5) * 3.1418 / 180 ) * (($Radius+$i)+$SpliceDistance) + $YPos; + $BorderX2 = cos($iAngle * 3.1418 / 180 ) * (($Radius+$i)+$SpliceDistance) + $XPos; + $BorderY2 = sin($iAngle * 3.1418 / 180 ) * (($Radius+$i)+$SpliceDistance) + $YPos; + + $this->drawLine($BorderX1,$BorderY1,$BorderX2,$BorderY2,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]); + } + } + } + + $TopPlots[$Key][] = $XCenterPos; + $TopPlots[$Key][] = $YCenterPos; + + $Angle = $iAngle + $SpliceDistanceRatio; + } + $PolyPlots = $TopPlots; + + /* Set array values type to float --- PHP Bug with imagefilledpolygon casting to integer */ + foreach ($TopPlots as $Key => $Value) + { foreach ($TopPlots[$Key] as $Key2 => $Value2) { settype($TopPlots[$Key][$Key2],"float"); } } + + /* Draw Top polygons */ + foreach ($TopPlots as $Key => $Value) + { + $C_GraphLo = imagecolorallocate($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]); + imagefilledpolygon($this->Picture,$PolyPlots[$Key],(count($PolyPlots[$Key])+1)/2,$C_GraphLo); + } + } + + /* This function draw a pseudo-3D pie chart */ + function drawPieGraph(&$Data,&$DataDescription,$XPos,$YPos,$Radius=100,$DrawLabels=PIE_NOLABEL,$EnhanceColors=TRUE,$Skew=60,$SpliceHeight=20,$SpliceDistance=0,$Decimals=0) + { + /* Validate the Data and DataDescription array */ + $this->validateDataDescription("drawPieGraph",$DataDescription,FALSE); + $this->validateData("drawPieGraph",$Data); + + /* Determine pie sum */ + $Series = 0; $PieSum = 0; $rPieSum = 0; + foreach ( $DataDescription["Values"] as $Key2 => $ColName ) + { + if ( $ColName != $DataDescription["Position"] ) + { + $Series++; + foreach ( $Data as $Key => $Values ) + if ( isset($Data[$Key][$ColName])) + { + if ( $Data[$Key][$ColName] == 0 ) + { $PieSum++; $iValues[] = 1; $rValues[] = 0; } + else + { $PieSum += $Data[$Key][$ColName]; $iValues[] = $Data[$Key][$ColName]; $iLabels[] = $Data[$Key][$DataDescription["Position"]]; $rValues[] = $Data[$Key][$ColName]; $rPieSum += $Data[$Key][$ColName];} + } + } + } + + /* Validate serie */ + if ( $Series != 1 ) + RaiseFatal("Pie chart can only accept one serie of data."); + + $SpliceDistanceRatio = $SpliceDistance; + $SkewHeight = ($Radius * $Skew) / 100; + $SpliceRatio = (360 - $SpliceDistanceRatio * count($iValues) ) / $PieSum; + $SplicePercent = 100 / $PieSum; + $rSplicePercent = 100 / $rPieSum; + + /* Calculate all polygons */ + $Angle = 0; $TopPlots = ""; $BotPlots = ""; $CDev = 5; + foreach($iValues as $Key => $Value) + { + $XCenterPos = cos(($Angle-$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $XPos; + $YCenterPos = sin(($Angle-$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $YPos; + $XCenterPos2 = cos(($Angle+$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $XPos; + $YCenterPos2 = sin(($Angle+$CDev+($Value*$SpliceRatio+$SpliceDistanceRatio)/2) * 3.1418 / 180 ) * $SpliceDistance + $YPos; + + $TopPlots[$Key][] = $XCenterPos; $BotPlots[$Key][] = $XCenterPos; + $TopPlots[$Key][] = $YCenterPos; $BotPlots[$Key][] = $YCenterPos + $SpliceHeight; + + /* Process labels position & size */ + if ( !($DrawLabels == PIE_NOLABEL) ) + { + $TAngle = $Angle+($Value*$SpliceRatio/2); + if ($DrawLabels == PIE_PERCENTAGE) + $Caption = (round($rValues[$Key] * pow(10,$Decimals) * $rSplicePercent)/pow(10,$Decimals))."%"; + elseif ($DrawLabels == PIE_LABELS) + $Caption = $iLabels[$Key]; + + $TX = cos(($TAngle) * 3.1418 / 180 ) * ($Radius + 10)+ $XPos; + + if ( $TAngle > 0 && $TAngle < 180 ) + $TY = sin(($TAngle) * 3.1418 / 180 ) * ($SkewHeight + 10) + $YPos + $SpliceHeight + 4; + else + $TY = sin(($TAngle) * 3.1418 / 180 ) * ($SkewHeight + 10) + $YPos + 4; + + if ( $TAngle > 90 && $TAngle < 270 ) + { + $Position = imageftbbox($this->FontSize,0,$this->FontName,$Caption,$bogus); + $TextWidth = $Position[2]-$Position[0]; + $TX = $TX - $TextWidth; + } + + $C_TextColor = imagecolorallocate($this->Picture,70,70,70); + imagettftext($this->Picture,$this->FontSize,0,$TX,$TY,$C_TextColor,$this->FontName,$Caption); + } + + /* Process pie slices */ + for($iAngle=$Angle;$iAngle<=$Angle+$Value*$SpliceRatio;$iAngle=$iAngle+.5) + { + $TopX = cos($iAngle * 3.1418 / 180 ) * $Radius + $XPos; + $TopY = sin($iAngle * 3.1418 / 180 ) * $SkewHeight + $YPos; + + $TopPlots[$Key][] = $TopX; $BotPlots[$Key][] = $TopX; + $TopPlots[$Key][] = $TopY; $BotPlots[$Key][] = $TopY + $SpliceHeight; + } + + $TopPlots[$Key][] = $XCenterPos2; $BotPlots[$Key][] = $XCenterPos2; + $TopPlots[$Key][] = $YCenterPos2; $BotPlots[$Key][] = $YCenterPos2 + $SpliceHeight; + + $Angle = $iAngle + $SpliceDistanceRatio; + } + + /* Draw Bottom polygons */ + foreach($iValues as $Key => $Value) + { + $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"],-20); + imagefilledpolygon($this->Picture,$BotPlots[$Key],(count($BotPlots[$Key])+1)/2,$C_GraphLo); + + for($j=0;$j<=count($BotPlots[$Key])-4;$j=$j+2) + $this->drawLine($BotPlots[$Key][$j],$BotPlots[$Key][$j+1],$BotPlots[$Key][$j+2],$BotPlots[$Key][$j+3],$this->Palette[$Key]["R"]-20,$this->Palette[$Key]["G"]-20,$this->Palette[$Key]["B"]-20); + } + + /* Draw pie layers */ + if ( $EnhanceColors ) { $ColorRatio = 30 / $SpliceHeight; } else { $ColorRatio = 25 / $SpliceHeight; } + for($i=$SpliceHeight-1;$i>=1;$i--) + { + foreach($iValues as $Key => $Value) + { + $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"],-10); + $Plots = ""; $Plot = 0; + foreach($TopPlots[$Key] as $Key2 => $Value2) + { + $Plot++; + if ( $Plot % 2 == 1 ) + $Plots[] = $Value2; + else + $Plots[] = $Value2+$i; + } + imagefilledpolygon($this->Picture,$Plots,(count($Plots)+1)/2,$C_GraphLo); + + $Index = count($Plots); + $ColorFactor = -20 + ($SpliceHeight - $i) * $ColorRatio; + $this->drawAntialiasPixel($Plots[0],$Plots[1],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor); + $this->drawAntialiasPixel($Plots[2],$Plots[3],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor); + $this->drawAntialiasPixel($Plots[$Index-4],$Plots[$Index-3],$this->Palette[$Key]["R"]+$ColorFactor,$this->Palette[$Key]["G"]+$ColorFactor,$this->Palette[$Key]["B"]+$ColorFactor); + } + } + + /* Draw Top polygons */ + for($Key=count($iValues)-1;$Key>=0;$Key--) + { + $C_GraphLo = $this->AllocateColor($this->Picture,$this->Palette[$Key]["R"],$this->Palette[$Key]["G"],$this->Palette[$Key]["B"]); + imagefilledpolygon($this->Picture,$TopPlots[$Key],(count($TopPlots[$Key])+1)/2,$C_GraphLo); + + if ( $EnhanceColors ) { $En = 10; } else { $En = 5; } + for($j=0;$j<=count($TopPlots[$Key])-4;$j=$j+2) + $this->drawLine($TopPlots[$Key][$j],$TopPlots[$Key][$j+1],$TopPlots[$Key][$j+2],$TopPlots[$Key][$j+3],$this->Palette[$Key]["R"]+$En,$this->Palette[$Key]["G"]+$En,$this->Palette[$Key]["B"]+$En); + } + } + + /* This function can be used to set the background color */ + function drawBackground($R,$G,$B) + { + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + $C_Background = imagecolorallocate($this->Picture,$R,$G,$B); + imagefilledrectangle($this->Picture,0,0,$this->XSize,$this->YSize,$C_Background); + } + + /* This function can be used to set the background color */ + function drawGraphAreaGradient($R,$G,$B,$Decay,$Target=TARGET_GRAPHAREA) + { + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + if ( $Target == TARGET_GRAPHAREA ) { $X1 = $this->GArea_X1+1; $X2 = $this->GArea_X2-1; $Y1 = $this->GArea_Y1+1; $Y2 = $this->GArea_Y2; } + if ( $Target == TARGET_BACKGROUND ) { $X1 = 0; $X2 = $this->XSize; $Y1 = 0; $Y2 = $this->YSize; } + + $YStep = ($Y2 - $Y1 - 2) / $Decay; + for($i=0;$i<=$Decay;$i++) + { + $R-=1;$G-=1;$B-=1; + $Yi1 = $Y1 + ( $i * $YStep ); + $Yi2 = ceil( $Yi1 + ( $i * $YStep ) + $YStep ); + if ( $Yi2 >= $Yi2 ) { $Yi2 = $Y2-1; } + + $C_Background = $this->AllocateColor($this->Picture,$R,$G,$B); + imagefilledrectangle($this->Picture,$X1,$Yi1,$X2,$Yi2,$C_Background); + } + } + + /* This function create a rectangle with antialias */ + function drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B) + { + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + $C_Rectangle = imagecolorallocate($this->Picture,$R,$G,$B); + + $X1=$X1-.2;$Y1=$Y1-.2; + $X2=$X2+.2;$Y2=$Y2+.2; + $this->drawLine($X1,$Y1,$X2,$Y1,$R,$G,$B); + $this->drawLine($X2,$Y1,$X2,$Y2,$R,$G,$B); + $this->drawLine($X2,$Y2,$X1,$Y2,$R,$G,$B); + $this->drawLine($X1,$Y2,$X1,$Y1,$R,$G,$B); + } + + /* This function create a filled rectangle with antialias */ + function drawFilledRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B,$DrawBorder=TRUE,$Alpha=100) + { + if ( $X2 > $X1 ) { list($X1, $X2) = array($X2, $X1); } + if ( $Y2 > $Y1 ) { list($Y1, $Y2) = array($Y2, $Y1); } + + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + if ( $Alpha == 100 ) + { + $C_Rectangle = imagecolorallocate($this->Picture,$R,$G,$B); + imagefilledrectangle($this->Picture,$X1,$Y1,$X2,$Y2,$C_Rectangle); + } + else + { + $LayerWidth = abs($X2-$X1)+2; + $LayerHeight = abs($Y2-$Y1)+2; + + $this->Layers[0] = imagecreatetruecolor($LayerWidth,$LayerHeight); + $C_White = imagecolorallocate($this->Layers[0],255,255,255); + imagefilledrectangle($this->Layers[0],0,0,$LayerWidth,$LayerHeight,$C_White); + imagecolortransparent($this->Layers[0],$C_White); + + $C_Rectangle = imagecolorallocate($this->Layers[0],$R,$G,$B); + imagefilledrectangle($this->Layers[0],1,1,$LayerWidth-1,$LayerHeight-1,$C_Rectangle); + + imagecopymerge($this->Picture,$this->Layers[0],min($X1,$X2)-1,min($Y1,$Y2)-1,0,0,$LayerWidth,$LayerHeight,$Alpha); + imagedestroy($this->Layers[0]); + } + + if ( $DrawBorder ) + $this->drawRectangle($X1,$Y1,$X2,$Y2,$R,$G,$B); + } + + /* This function create a rectangle with rounded corners and antialias */ + function drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B) + { + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + $C_Rectangle = imagecolorallocate($this->Picture,$R,$G,$B); + + $Step = 90 / ((3.1418 * $Radius)/2); + + for($i=0;$i<=90;$i=$i+$Step) + { + $X = cos(($i+180)*3.1418/180) * $Radius + $X1 + $Radius; + $Y = sin(($i+180)*3.1418/180) * $Radius + $Y1 + $Radius; + $this->drawAntialiasPixel($X,$Y,$R,$G,$B); + + $X = cos(($i-90)*3.1418/180) * $Radius + $X2 - $Radius; + $Y = sin(($i-90)*3.1418/180) * $Radius + $Y1 + $Radius; + $this->drawAntialiasPixel($X,$Y,$R,$G,$B); + + $X = cos(($i)*3.1418/180) * $Radius + $X2 - $Radius; + $Y = sin(($i)*3.1418/180) * $Radius + $Y2 - $Radius; + $this->drawAntialiasPixel($X,$Y,$R,$G,$B); + + $X = cos(($i+90)*3.1418/180) * $Radius + $X1 + $Radius; + $Y = sin(($i+90)*3.1418/180) * $Radius + $Y2 - $Radius; + $this->drawAntialiasPixel($X,$Y,$R,$G,$B); + } + + $X1=$X1-.2;$Y1=$Y1-.2; + $X2=$X2+.2;$Y2=$Y2+.2; + $this->drawLine($X1+$Radius,$Y1,$X2-$Radius,$Y1,$R,$G,$B); + $this->drawLine($X2,$Y1+$Radius,$X2,$Y2-$Radius,$R,$G,$B); + $this->drawLine($X2-$Radius,$Y2,$X1+$Radius,$Y2,$R,$G,$B); + $this->drawLine($X1,$Y2-$Radius,$X1,$Y1+$Radius,$R,$G,$B); + } + + /* This function create a filled rectangle with rounded corners and antialias */ + function drawFilledRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$R,$G,$B) + { + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + $C_Rectangle = imagecolorallocate($this->Picture,$R,$G,$B); + + $Step = 90 / ((3.1418 * $Radius)/2); + + for($i=0;$i<=90;$i=$i+$Step) + { + $Xi1 = cos(($i+180)*3.1418/180) * $Radius + $X1 + $Radius; + $Yi1 = sin(($i+180)*3.1418/180) * $Radius + $Y1 + $Radius; + + $Xi2 = cos(($i-90)*3.1418/180) * $Radius + $X2 - $Radius; + $Yi2 = sin(($i-90)*3.1418/180) * $Radius + $Y1 + $Radius; + + $Xi3 = cos(($i)*3.1418/180) * $Radius + $X2 - $Radius; + $Yi3 = sin(($i)*3.1418/180) * $Radius + $Y2 - $Radius; + + $Xi4 = cos(($i+90)*3.1418/180) * $Radius + $X1 + $Radius; + $Yi4 = sin(($i+90)*3.1418/180) * $Radius + $Y2 - $Radius; + + imageline($this->Picture,$Xi1,$Yi1,$X1+$Radius,$Yi1,$C_Rectangle); + imageline($this->Picture,$X2-$Radius,$Yi2,$Xi2,$Yi2,$C_Rectangle); + imageline($this->Picture,$X2-$Radius,$Yi3,$Xi3,$Yi3,$C_Rectangle); + imageline($this->Picture,$Xi4,$Yi4,$X1+$Radius,$Yi4,$C_Rectangle); + + $this->drawAntialiasPixel($Xi1,$Yi1,$R,$G,$B); + $this->drawAntialiasPixel($Xi2,$Yi2,$R,$G,$B); + $this->drawAntialiasPixel($Xi3,$Yi3,$R,$G,$B); + $this->drawAntialiasPixel($Xi4,$Yi4,$R,$G,$B); + } + + imagefilledrectangle($this->Picture,$X1,$Y1+$Radius,$X2,$Y2-$Radius,$C_Rectangle); + imagefilledrectangle($this->Picture,$X1+$Radius,$Y1,$X2-$Radius,$Y2,$C_Rectangle); + + $X1=$X1-.2;$Y1=$Y1-.2; + $X2=$X2+.2;$Y2=$Y2+.2; + $this->drawLine($X1+$Radius,$Y1,$X2-$Radius,$Y1,$R,$G,$B); + $this->drawLine($X2,$Y1+$Radius,$X2,$Y2-$Radius,$R,$G,$B); + $this->drawLine($X2-$Radius,$Y2,$X1+$Radius,$Y2,$R,$G,$B); + $this->drawLine($X1,$Y2-$Radius,$X1,$Y1+$Radius,$R,$G,$B); + } + + /* This function create a circle with antialias */ + function drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0) + { + if ( $Width == 0 ) { $Width = $Height; } + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + $C_Circle = imagecolorallocate($this->Picture,$R,$G,$B); + $Step = 360 / (2 * 3.1418 * max($Width,$Height)); + + for($i=0;$i<=360;$i=$i+$Step) + { + $X = cos($i*3.1418/180) * $Height + $Xc; + $Y = sin($i*3.1418/180) * $Width + $Yc; + $this->drawAntialiasPixel($X,$Y,$R,$G,$B); + } + } + + /* This function create a filled circle/ellipse with antialias */ + function drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width=0) + { + if ( $Width == 0 ) { $Width = $Height; } + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + $C_Circle = imagecolorallocate($this->Picture,$R,$G,$B); + $Step = 360 / (2 * 3.1418 * max($Width,$Height)); + + for($i=90;$i<=270;$i=$i+$Step) + { + $X1 = cos($i*3.1418/180) * $Height + $Xc; + $Y1 = sin($i*3.1418/180) * $Width + $Yc; + $X2 = cos((180-$i)*3.1418/180) * $Height + $Xc; + $Y2 = sin((180-$i)*3.1418/180) * $Width + $Yc; + + $this->drawAntialiasPixel($X1-1,$Y1-1,$R,$G,$B); + $this->drawAntialiasPixel($X2-1,$Y2-1,$R,$G,$B); + + if ( ($Y1-1) > $Yc - max($Width,$Height) ) + imageline($this->Picture,$X1,$Y1-1,$X2-1,$Y2-1,$C_Circle); + } + } + + /* This function will draw a filled ellipse */ + function drawEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B) + { $this->drawCircle($Xc,$Yc,$Height,$R,$G,$B,$Width); } + + /* This function will draw an ellipse */ + function drawFilledEllipse($Xc,$Yc,$Height,$Width,$R,$G,$B) + { $this->drawFilledCircle($Xc,$Yc,$Height,$R,$G,$B,$Width); } + + /* This function create a line with antialias */ + function drawLine($X1,$Y1,$X2,$Y2,$R,$G,$B,$GraphFunction=FALSE) + { + if ( $this->LineDotSize > 1 ) { $this->drawDottedLine($X1,$Y1,$X2,$Y2,$this->LineDotSize,$R,$G,$B,$GraphFunction); return(0); } + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + $Distance = sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1)); + if ( $Distance == 0 ) + return(-1); + $XStep = ($X2-$X1) / $Distance; + $YStep = ($Y2-$Y1) / $Distance; + + for($i=0;$i<=$Distance;$i++) + { + $X = $i * $XStep + $X1; + $Y = $i * $YStep + $Y1; + + if ( ($X >= $this->GArea_X1 && $X <= $this->GArea_X2 && $Y >= $this->GArea_Y1 && $Y <= $this->GArea_Y2) || !$GraphFunction ) + { + if ( $this->LineWidth == 1 ) + $this->drawAntialiasPixel($X,$Y,$R,$G,$B); + else + { + $StartOffset = -($this->LineWidth/2); $EndOffset = ($this->LineWidth/2); + for($j=$StartOffset;$j<=$EndOffset;$j++) + $this->drawAntialiasPixel($X+$j,$Y+$j,$R,$G,$B); + } + } + } + } + + /* This function create a line with antialias */ + function drawDottedLine($X1,$Y1,$X2,$Y2,$DotSize,$R,$G,$B,$GraphFunction=FALSE) + { + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + $Distance = sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1)); + + $XStep = ($X2-$X1) / $Distance; + $YStep = ($Y2-$Y1) / $Distance; + + $DotIndex = 0; + for($i=0;$i<=$Distance;$i++) + { + $X = $i * $XStep + $X1; + $Y = $i * $YStep + $Y1; + + if ( $DotIndex <= $DotSize) + { + if ( ($X >= $this->GArea_X1 && $X <= $this->GArea_X2 && $Y >= $this->GArea_Y1 && $Y <= $this->GArea_Y2) || !$GraphFunction ) + { + if ( $this->LineWidth == 1 ) + $this->drawAntialiasPixel($X,$Y,$R,$G,$B); + else + { + $StartOffset = -($this->LineWidth/2); $EndOffset = ($this->LineWidth/2); + for($j=$StartOffset;$j<=$EndOffset;$j++) + $this->drawAntialiasPixel($X+$j,$Y+$j,$R,$G,$B); + } + } + } + + $DotIndex++; + if ( $DotIndex == $DotSize * 2 ) + $DotIndex = 0; + } + } + + /* Load a PNG file and draw it over the chart */ + function drawFromPNG($FileName,$X,$Y,$Alpha=100) + { $this->drawFromPicture(1,$FileName,$X,$Y,$Alpha); } + + /* Load a GIF file and draw it over the chart */ + function drawFromGIF($FileName,$X,$Y,$Alpha=100) + { $this->drawFromPicture(2,$FileName,$X,$Y,$Alpha); } + + /* Load a JPEG file and draw it over the chart */ + function drawFromJPG($FileName,$X,$Y,$Alpha=100) + { $this->drawFromPicture(3,$FileName,$X,$Y,$Alpha); } + + /* Generic loader function for external pictures */ + function drawFromPicture($PicType,$FileName,$X,$Y,$Alpha=100) + { + if ( file_exists($FileName)) + { + $Infos = getimagesize($FileName); + $Width = $Infos[0]; + $Height = $Infos[1]; + if ( $PicType == 1 ) { $Raster = imagecreatefrompng($FileName); } + if ( $PicType == 2 ) { $Raster = imagecreatefromgif($FileName); } + if ( $PicType == 3 ) { $Raster = imagecreatefromjpeg($FileName); } + + imagecopymerge($this->Picture,$Raster,$X,$Y,0,0,$Width,$Height,$Alpha); + imagedestroy($Raster); + } + } + + /* Draw an alpha pixel */ + function drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B) + { + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + if ( $X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize ) + return(-1); + + $RGB2 = imagecolorat($this->Picture, $X, $Y); + $R2 = ($RGB2 >> 16) & 0xFF; + $G2 = ($RGB2 >> 8) & 0xFF; + $B2 = $RGB2 & 0xFF; + + $iAlpha = (100 - $Alpha)/100; + $Alpha = $Alpha / 100; + + $Ra = floor($R*$Alpha+$R2*$iAlpha); + $Ga = floor($G*$Alpha+$G2*$iAlpha); + $Ba = floor($B*$Alpha+$B2*$iAlpha); + + $C_Aliased = imagecolorallocate($this->Picture,$Ra,$Ga,$Ba); + imagesetpixel($this->Picture,$X,$Y,$C_Aliased); + } + + /* Color helper */ + function AllocateColor($Picture,$R,$G,$B,$Factor=0) + { + $R = $R + $Factor; + $G = $G + $Factor; + $B = $B + $Factor; + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + return(imagecolorallocate($Picture,$R,$G,$B)); + } + + /* Add a border to the picture */ + function addBorder($Size=3,$R=0,$G=0,$B=0) + { + $Width = $this->XSize+2*$Size; + $Height = $this->YSize+2*$Size; + + $Resampled = imagecreatetruecolor($Width,$Height); + $C_Background = imagecolorallocate($Resampled,$R,$G,$B); + imagefilledrectangle($Resampled,0,0,$Width,$Height,$C_Background); + + imagecopy($Resampled,$this->Picture,$Size,$Size,0,0,$this->XSize,$this->YSize); + imagedestroy($this->Picture); + + $this->XSize = $Width; + $this->YSize = $Height; + + $this->Picture = imagecreatetruecolor($this->XSize,$this->YSize); + $C_White = imagecolorallocate($this->Picture,255,255,255); + imagefilledrectangle($this->Picture,0,0,$this->XSize,$this->YSize,$C_White); + imagecolortransparent($this->Picture,$C_White); + imagecopy($this->Picture,$Resampled,0,0,0,0,$this->XSize,$this->YSize); + } + + /* Render the current picture to a file */ + function Render($FileName) + { + if ( $this->ErrorReporting ) + $this->printErrors($this->ErrorInterface); + + /* Save image map if requested */ + if ( $this->BuildMap ) + $this->SaveImageMap(); + + imagepng($this->Picture,$FileName); + } + + /* Render the current picture to STDOUT */ + function Stroke() + { + if ( $this->ErrorReporting ) + $this->printErrors("GD"); + + /* Save image map if requested */ + if ( $this->BuildMap ) + $this->SaveImageMap(); + + header('Content-type: image/png'); + imagepng($this->Picture); + } + + /* Private functions for internal processing */ + function drawAntialiasPixel($X,$Y,$R,$G,$B) + { + if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; } + if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; } + if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; } + + $Plot = ""; + $Xi = floor($X); + $Yi = floor($Y); + + if ( $Xi == $X && $Yi == $Y) + { + /* $this->drawAlphaPixel($Xi,$Yi,0,$R,$G,$B); */ + $C_Aliased = imagecolorallocate($this->Picture,$R,$G,$B); + imagesetpixel($this->Picture,$X,$Y,$C_Aliased); + } + else + { + $Alpha1 = (1 - ($X - floor($X))) * (1 - ($Y - floor($Y))) * 100; + if ( $Alpha1 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi,$Alpha1,$R,$G,$B); } + + $Alpha2 = ($X - floor($X)) * (1 - ($Y - floor($Y))) * 100; + if ( $Alpha2 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi,$Alpha2,$R,$G,$B); } + + $Alpha3 = (1 - ($X - floor($X))) * ($Y - floor($Y)) * 100; + if ( $Alpha3 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi+1,$Alpha3,$R,$G,$B); } + + $Alpha4 = ($X - floor($X)) * ($Y - floor($Y)) * 100; + if ( $Alpha4 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi+1,$Alpha4,$R,$G,$B); } + } + } + + /* Validate data contained in the description array */ + function validateDataDescription($FunctionName,&$DataDescription,$DescriptionRequired=TRUE) + { + if (!isset($DataDescription["Position"])) + { + $this->Errors[] = "[Warning] ".$FunctionName." - Y Labels are not set."; + $DataDescription["Position"] = "Name"; + } + + if ( $DescriptionRequired ) + { + if (!isset($DataDescription["Description"])) + { + $this->Errors[] = "[Warning] ".$FunctionName." - Series descriptions are not set."; + foreach($DataDescription["Values"] as $key => $Value) + { + $DataDescription["Description"][$Value] = $Value; + } + } + + if (count($DataDescription["Description"]) < count($DataDescription["Values"])) + { + $this->Errors[] = "[Warning] ".$FunctionName." - Some series descriptions are not set."; + foreach($DataDescription["Values"] as $key => $Value) + { + if ( !isset($DataDescription["Description"][$Value])) + $DataDescription["Description"][$Value] = $Value; + } + } + } + } + + /* Validate data contained in the data array */ + function validateData($FunctionName,&$Data) + { + $DataSummary = ""; + + foreach($Data as $key => $Values) + { + foreach($Values as $key2 => $Value) + { + if (!isset($DataSummary[$key2])) + $DataSummary[$key2] = 1; + else + $DataSummary[$key2]++; + } + } + + if ( max($DataSummary) == 0 ) + $this->Errors[] = "[Warning] ".$FunctionName." - No data set."; + + foreach($DataSummary as $key => $Value) + { + if ($Value < max($DataSummary)) + { + $this->Errors[] = "[Warning] ".$FunctionName." - Missing data in serie ".$key."."; + } + } + } + + /* Print all error messages on the CLI or graphically */ + function printErrors($Mode="CLI") + { + if (count($this->Errors) == 0) + return(0); + + if ( $Mode == "CLI" ) + { + foreach($this->Errors as $key => $Value) + echo $Value."\r\n"; + } + elseif ( $Mode == "GD" ) + { + $this->setLineStyle($Width=1); + $MaxWidth = 0; + foreach($this->Errors as $key => $Value) + { + $Position = imageftbbox($this->ErrorFontSize,0,$this->ErrorFontName,$Value,$bogus); + $TextWidth = $Position[2]-$Position[0]; + if ( $TextWidth > $MaxWidth ) { $MaxWidth = $TextWidth; } + } + $this->drawFilledRoundedRectangle($this->XSize-($MaxWidth+20),$this->YSize-(20+(($this->ErrorFontSize+4)*count($this->Errors))),$this->XSize-10,$this->YSize-10,6,233,185,185); + $this->drawRoundedRectangle($this->XSize-($MaxWidth+20),$this->YSize-(20+(($this->ErrorFontSize+4)*count($this->Errors))),$this->XSize-10,$this->YSize-10,6,193,145,145); + + $C_TextColor = imagecolorallocate($this->Picture,133,85,85); + $YPos = $this->YSize - (18 + (count($this->Errors)-1) * ($this->ErrorFontSize + 4)); + foreach($this->Errors as $key => $Value) + { + imagettftext($this->Picture,$this->ErrorFontSize,0,$this->XSize-($MaxWidth+15),$YPos,$C_TextColor,$this->ErrorFontName,$Value); + $YPos = $YPos + ($this->ErrorFontSize + 4); + } + } + } + + /* Activate the image map creation process */ + function setImageMap($Mode=TRUE,$GraphID="MyGraph") + { + $this->BuildMap = $Mode; + $this->MapID = $GraphID; + } + + /* Add a box into the image map */ + function addToImageMap($X1,$Y1,$X2,$Y2,$SerieName,$Value,$CallerFunction) + { + if ( $this->MapFunction == NULL || $this->MapFunction == $CallerFunction ) + { + $this->ImageMap[] = round($X1).",".round($Y1).",".round($X2).",".round($Y2).",".$SerieName.",".$Value; + $this->MapFunction = $CallerFunction; + } + } + + /* Load and cleanup the image map from disk */ + function getImageMap($MapName,$Flush=TRUE) + { + /* Strip HTML query strings */ + $Values = $this->tmpFolder.$MapName; + $Value = split("\?",$Values); + $FileName = $Value[0]; + + if ( file_exists($FileName) ) + { + $Handle = fopen($FileName, "r"); + $MapContent = fread($Handle, filesize($FileName)); + fclose($Handle); + echo $MapContent; + + if ( $Flush ) + unlink($FileName); + + exit(); + } + else + { + header("HTTP/1.0 404 Not Found"); + exit(); + } + } + + /* Save the image map to the disk */ + function SaveImageMap() + { + if ( !$this->BuildMap ) { return(-1); } + + if ( $this->ImageMap == NULL ) + { + $this->Errors[] = "[Warning] SaveImageMap - Image map is empty."; + return(-1); + } + + $Handle = fopen($this->tmpFolder.$this->MapID, 'w'); + if ( !$Handle ) + { + $this->Errors[] = "[Warning] SaveImageMap - Cannot save the image map."; + return(-1); + } + else + { + foreach($this->ImageMap as $Key => $Value) + fwrite($Handle, htmlentities($Value)."\r"); + } + fclose ($Handle); + } + + /* Convert seconds to a time format string */ + function ToTime($Value) + { + $Hour = floor($Value/3600); + $Minute = floor(($Value - $Hour*3600)/60); + $Second = floor($Value - $Hour*3600 - $Minute*60); + + if (strlen($Hour) == 1 ) { $Hour = "0".$Hour; } + if (strlen($Minute) == 1 ) { $Minute = "0".$Minute; } + if (strlen($Second) == 1 ) { $Second = "0".$Second; } + + return($Hour.":".$Minute.":".$Second); + } + + /* Convert to metric system */ + function ToMetric($Value) + { + $Go = floor($Value/1000000000); + $Mo = floor(($Value - $Go*1000000000)/1000000); + $Ko = floor(($Value - $Go*1000000000 - $Mo*1000000)/1000); + $o = floor($Value - $Go*1000000000 - $Mo*1000000 - $Ko*1000); + + if ($Go != 0) { return($Go.".".$Mo."g"); } + if ($Mo != 0) { return($Mo.".".$ko."m"); } + if ($Ko != 0) { return($Ko.".".$o)."k"; } + return($o); + } + + /* Set date format for axis labels */ + function setDateFormat($Format) + { + $this->DateFormat = $Format; + } + + /* Convert TS to a date format string */ + function ToDate($Value) + { + return(date($this->DateFormat,$Value)); + } + } + + function RaiseFatal($Message) + { + echo "[FATAL] ".$Message."\r\n"; + exit(); + } +?> diff --git a/clients/GD/FSMon/pChart/pData.class b/clients/GD/FSMon/pChart/pData.class new file mode 100644 index 0000000..8e1938d --- /dev/null +++ b/clients/GD/FSMon/pChart/pData.class @@ -0,0 +1,260 @@ +. + + Class initialisation : + pData() + Data populating methods : + ImportFromCSV($FileName,$Delimiter=",",$DataColumns=-1,$HasHeader=FALSE,$DataName=-1) + AddPoint($Value,$Serie="Serie1",$Description="") + Series manipulation methods : + AddSerie($SerieName="Serie1") + AddAllSeries() + RemoveSerie($SerieName="Serie1") + SetAbsciseLabelSerie($SerieName = "Name") + SetSerieName($Name,$SerieName="Serie1") + + SetSerieSymbol($Name,$Symbol) + SetXAxisName($Name="X Axis") + SetYAxisName($Name="Y Axis") + SetXAxisFormat($Format="number") + SetYAxisFormat($Format="number") + SetXAxisUnit($Unit="") + SetYAxisUnit($Unit="") + removeSerieName($SerieName) + removeAllSeries() + Data retrieval methods : + GetData() + GetDataDescription() + */ + + /* pData class definition */ + class pData + { + var $Data; + var $DataDescription; + + function pData() + { + $this->Data = ""; + $this->DataDescription = ""; + $this->DataDescription["Position"] = "Name"; + $this->DataDescription["Format"]["X"] = "number"; + $this->DataDescription["Format"]["Y"] = "number"; + $this->DataDescription["Unit"]["X"] = NULL; + $this->DataDescription["Unit"]["Y"] = NULL; + } + + function ImportFromCSV($FileName,$Delimiter=",",$DataColumns=-1,$HasHeader=FALSE,$DataName=-1) + { + $handle = @fopen($FileName,"r"); + if ($handle) + { + $HeaderParsed = FALSE; + while (!feof($handle)) + { + $buffer = fgets($handle, 4096); + $buffer = str_replace(chr(10),"",$buffer); + $buffer = str_replace(chr(13),"",$buffer); + $Values = split($Delimiter,$buffer); + + if ( $buffer != "" ) + { + if ( $HasHeader == TRUE && $HeaderParsed == FALSE ) + { + if ( $DataColumns == -1 ) + { + $ID = 1; + foreach($Values as $key => $Value) + { $this->SetSerieName($Value,"Serie".$ID); $ID++; } + } + else + { + $SerieName = ""; + + foreach($DataColumns as $key => $Value) + $this->SetSerieName($Values[$Value],"Serie".$Value); + } + $HeaderParsed = TRUE; + } + else + { + if ( $DataColumns == -1 ) + { + $ID = 1; + foreach($Values as $key => $Value) + { $this->AddPoint(intval($Value),"Serie".$ID); $ID++; } + } + else + { + $SerieName = ""; + if ( $DataName != -1 ) + $SerieName = $Values[$DataName]; + + foreach($DataColumns as $key => $Value) + $this->AddPoint($Values[$Value],"Serie".$Value,$SerieName); + } + } + } + } + fclose($handle); + } + } + + function AddPoint($Value,$Serie="Serie1",$Description="") + { + if (is_array($Value) && count($Value) == 1) + $Value = $Value[0]; + + $ID = 0; + for($i=0;$i<=count($this->Data);$i++) + { if(isset($this->Data[$i][$Serie])) { $ID = $i+1; } } + + if ( count($Value) == 1 ) + { + $this->Data[$ID][$Serie] = $Value; + if ( $Description != "" ) + $this->Data[$ID]["Name"] = $Description; + elseif (!isset($this->Data[$ID]["Name"])) + $this->Data[$ID]["Name"] = $ID; + } + else + { + foreach($Value as $key => $Val) + { + $this->Data[$ID][$Serie] = $Val; + if (!isset($this->Data[$ID]["Name"])) + $this->Data[$ID]["Name"] = $ID; + $ID++; + } + } + } + + function AddSerie($SerieName="Serie1") + { + if ( !isset($this->DataDescription["Values"]) ) + { + $this->DataDescription["Values"][] = $SerieName; + } + else + { + $Found = FALSE; + foreach($this->DataDescription["Values"] as $key => $Value ) + if ( $Value == $SerieName ) { $Found = TRUE; } + + if ( !$Found ) + $this->DataDescription["Values"][] = $SerieName; + } + } + + function AddAllSeries() + { + unset($this->DataDescription["Values"]); + + if ( isset($this->Data[0]) ) + { + foreach($this->Data[0] as $Key => $Value) + { + if ( $Key != "Name" ) + $this->DataDescription["Values"][] = $Key; + } + } + } + + function RemoveSerie($SerieName="Serie1") + { + if ( !isset($this->DataDescription["Values"]) ) + return(0); + + $Found = FALSE; + foreach($this->DataDescription["Values"] as $key => $Value ) + { + if ( $Value == $SerieName ) + unset($this->DataDescription["Values"][$key]); + } + } + + function SetAbsciseLabelSerie($SerieName = "Name") + { + $this->DataDescription["Position"] = $SerieName; + } + + function SetSerieName($Name,$SerieName="Serie1") + { + $this->DataDescription["Description"][$SerieName] = $Name; + } + + function SetXAxisName($Name="X Axis") + { + $this->DataDescription["Axis"]["X"] = $Name; + } + + function SetYAxisName($Name="Y Axis") + { + $this->DataDescription["Axis"]["Y"] = $Name; + } + + function SetXAxisFormat($Format="number") + { + $this->DataDescription["Format"]["X"] = $Format; + } + + function SetYAxisFormat($Format="number") + { + $this->DataDescription["Format"]["Y"] = $Format; + } + + function SetXAxisUnit($Unit="") + { + $this->DataDescription["Unit"]["X"] = $Unit; + } + + function SetYAxisUnit($Unit="") + { + $this->DataDescription["Unit"]["Y"] = $Unit; + } + + function SetSerieSymbol($Name,$Symbol) + { + $this->DataDescription["Symbol"][$Name] = $Symbol; + } + + function removeSerieName($SerieName) + { + if ( isset($this->DataDescription["Description"][$SerieName]) ) + unset($this->DataDescription["Description"][$SerieName]); + } + + function removeAllSeries() + { + foreach($this->DataDescription["Values"] as $Key => $Value) + unset($this->DataDescription["Values"][$Key]); + } + + function GetData() + { + return($this->Data); + } + + function GetDataDescription() + { + return($this->DataDescription); + } + } +?> \ No newline at end of file diff --git a/clients/GD/FSMon/testgraph.php b/clients/GD/FSMon/testgraph.php new file mode 100644 index 0000000..f52de98 --- /dev/null +++ b/clients/GD/FSMon/testgraph.php @@ -0,0 +1,62 @@ +AddPoint (0, "Free", 1); +// $DataSet->AddPoint (0, "Used", 1); +// $DataSet->AddPoint (70, "Free", 2); +// $DataSet->AddPoint (30, "Used", 2); +// $DataSet->AddPoint (80, "Free", 3); +// $DataSet->AddPoint (20, "Used", 3); +// $DataSet->AddPoint (90, "Free", 4); +// $DataSet->AddPoint (10, "Used", 4); + +// Used first +$DataSet->AddPoint (0, "Used", 1); +$DataSet->AddPoint (70, "Used", 2); +$DataSet->AddPoint (80, "Used", 3); +$DataSet->AddPoint (90, "Used", 4); +$DataSet->AddPoint (0, "Free", 1); +$DataSet->AddPoint (30, "Free", 2); +$DataSet->AddPoint (20, "Free", 3); +$DataSet->AddPoint (10, "Free", 4); + +$DataSet->AddAllSeries(); +$DataSet->SetAbsciseLabelSerie(); + +// Initialise the graph +$Test = new pChart (700, 280); + +$Test->setColorPalette (1, 0, 255, 0); +$Test->setColorPalette (0, 255, 0, 0); + +$Test->drawGraphAreaGradient (100, 150, 175, 100, TARGET_BACKGROUND); +$Test->setFontProperties ("$fonts/tahoma.ttf", 8); +$Test->setGraphArea (50, 30, 680, 200); +$Test->drawRoundedRectangle (5, 5, 695, 275, 5, 230, 230, 230); +$Test->drawGraphAreaGradient (162, 183, 202, 50); +$Test->drawScale ($DataSet->GetData (), $DataSet->GetDataDescription (), SCALE_ADDALL, 200, 200, 200, true, 70, 2, true); +$Test->drawGrid (4, true, 230, 230, 230, 50); + +// Draw the 0 line +$Test->setFontProperties ("$fonts/tahoma.ttf", 6); +$Test->drawTreshold (0, 143, 55, 72, true, true); + +// Draw the bar graph +$Test->drawStackedBarGraph ($DataSet->GetData (), $DataSet->GetDataDescription (), 75); + +// Finish the graph +$Test->setFontProperties ("$fonts/tahoma.ttf",8); +$Test->drawLegend (610, 35, $DataSet->GetDataDescription (), 130, 180, 205); +$Test->setFontProperties ("$fonts/tahoma.ttf", 10); +$Test->drawTitle (50, 22, "$system:$mount", 255, 255, 255, 675); +$Test->Stroke (); +?> \ No newline at end of file diff --git a/clients/GD/cqtool.tar.gz b/clients/GD/cqtool.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..8c82abd1d0ac08521715f0775ac54ba88691e6f8 GIT binary patch literal 10457 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 PoB#d~S of ClearSCM, Inc. +END + + my $desc = $_createHelpDeskUI->Dialog ( + -title => "About $ME", + -text => $text, + -buttons => [ "OK" ], + ); + + $desc->Show; + } # _helpAbout + + #--------------------------------------------------------------------------- + # _displayValues (): Displays the contents for %hd hash + #--------------------------------------------------------------------------- + sub _displayValues () { + foreach (keys %hd) { + if ($hd{$_}) { + display "$_: $hd{$_}"; + } else { + display "$_: undef"; + } # if + } # foreach + } # _displayValues + + #--------------------------------------------------------------------------- + # _getChoices (): For a given $entity and $fieldname, this routine returns + # the given choice list from Clearquest. + #--------------------------------------------------------------------------- + sub _getChoices ($$) { + my ($entity, $fieldname) = @_; + + return @{$entity->GetFieldChoiceList ($fieldname)}; + } # _getChoices + + #--------------------------------------------------------------------------- + # _destroyHelpDeskUI (): Destroys the current HelpDesk UI recycling Tk + # objects + #--------------------------------------------------------------------------- + sub _destroyHelpDeskUI () { + # Destroy all globals created + destroy $_submit; + destroy $_requestor; + destroy $_location; + destroy $_category; + destroy $_related_version; + destroy $_platform; + destroy $_requestor_priority; + destroy $_createHelpDeskUI; + + $_requestor = + $_location = + $_category = + $_related_version = + $_platform = + $_requestor_priority = + $_submit = + $_createHelpDeskUI = undef; + + %hd = (); + } # _destroyHelpDeskUI + + #--------------------------------------------------------------------------- + # _submit (): Actually creates the WOR given the filled out %hd hash. + #--------------------------------------------------------------------------- + sub _submit () { + debug "Creating Help Desk Ticket..."; + + # Change requestor from a format of "lastname, firstname (badge)" -> badge + if ($hd{requestor} =~ /\((\w*)\)$/) { + $hd{requestor} = $1; + } # if + + _displayValues if get_debug; + + my $new_id = CQTool::submitHelpDesk ($CQTool::entity, %hd); + + display $new_id if $new_id; + + _destroyHelpDeskUI; + + return $new_id; + } # _submit + + #--------------------------------------------------------------------------- + # _setSubmitButton (): Sets the submit button to active only if all required + # fields have values. + #--------------------------------------------------------------------------- + sub _setSubmitButton (;$) { + my ($headline) = @_; + + return if !$_submit; + + # Check to see if we can activate the submit button + my $state = "normal"; + + foreach (@CQTool::hd_required_fields) { + if ($_ eq "headline") { + if (defined $headline) { + if ($headline eq "") { + $state = "disable"; + last; + } else { + next; + } # if + } # if + } # if + + if (!$hd{$_} or $hd{$_} eq "") { + $state = "disable"; + last; + } # if + } # foreach + + $_submit->configure ( + -state => $state, + ); + } # _setSubmitButton + + #--------------------------------------------------------------------------- + # _validateText (): Gets the text from the MyText widget and sets the submit + # button + #--------------------------------------------------------------------------- + sub _validatetext { + my ($text) = @_; + + $hd{description} = $text->get_text; + chomp $hd{description}; + + _setSubmitButton $text; + + return 1; + } # _validatetext + + #--------------------------------------------------------------------------- + # _validateEntry (): Gets the text from the headline widget and sets the + # submit button + #--------------------------------------------------------------------------- + sub _validateentry { + my ($entry) = @_; + + _setSubmitButton $entry; + + return 1; + } # _validateentry + + #--------------------------------------------------------------------------- + # _createDropDown (): Creates a dropdown widget in $parent in a grid at the + # $x, $y coordinates with a $label and a $value, using + # dropdown @values and a $refresh procedure. + #--------------------------------------------------------------------------- + sub _createDropDown ($$$$$$@) { + my ($parent, $x, $y, $label, $refresh, $value, @values) = @_; + + $parent->Label ( + -width => length $label, + -text => "$label:", + )->grid ( + -row => $x, + -column => $y, + -sticky => "e", + ); + + return $parent->Optionmenu ( + -activeforeground => $EDIT_FOREGROUND, + -activebackground => $EDIT_BACKGROUND, + -command => \&$refresh, + -variable => $value, + -options => \@values, + )->grid ( + -row => $x, + -column => $y + 1, + -sticky => "w", + ); + } # _createDropDown + + #--------------------------------------------------------------------------- + # _createBrowseEntry (): Creates a dropdown like widget which drops down a + # scrollable list in $parent with a $label, $refresh + # procedure, setting $value with the choice from + # @values. + #--------------------------------------------------------------------------- + sub _createBrowseEntry ($$$$$$@) { + my ($parent, $x, $y, $label, $refresh, $value, @values) = @_; + + $parent->Label ( + -width => length $label, + -text => "$label:", + )->grid ( + -row => $x, + -column => $y, + -sticky => "e", + ); + + my $longest_item = 0; + + foreach (@values) { + $longest_item = length $_ if length $_ > $longest_item; + } # if + + my $browse_entry = $parent->BrowseEntry ( + -browsecmd => \&$refresh, + -variable => $value, + -width => $longest_item, + )->grid ( + -row => $x, + -column => $y + 1, + -sticky => "w", + ); + + my $i = 0; + + foreach (@values) { + $browse_entry->insert ($i++, $_); + } # foreach + + return $browse_entry; + } # _createBrowseEntry + + #--------------------------------------------------------------------------- + # _createTextField (): Creates a text field widget in $parent with a $label + # and a $value, using a $maxlen and a $validate + # procedure. + #--------------------------------------------------------------------------- + sub _createTextField ($$$$$) { + my ($parent, $label, $value, $maxlen, $validate) = @_; + + $parent->Label ( + -text => "$label:", + -justify => "right", + -width => 10, + )->pack ( + -side => "left", + -anchor => "e", + ); + + $parent->Entry ( + -foreground => $EDIT_FOREGROUND, + -background => $EDIT_BACKGROUND, + -width => $maxlen, + -justify => "left", + -textvariable => $value, + -validate => "key", + -validatecommand => \&$validate, + )->pack ( + -side => "left", + -padx => 5, + -anchor => "e", + ); + } # _createTextField + + #--------------------------------------------------------------------------- + # _createText (): Creates a multiline text field widget in $parent with a + # $label and a $value, using the specified $rows and $cols + # and a $validate procedure. + #--------------------------------------------------------------------------- + sub _createText ($$$$$$) { + my ($parent, $label, $value, $rows, $cols, $validate) = @_; + + $parent->Label ( + -text => "$label:", + -justify => "right", + -width => 10, + )->pack ( + -side => "left",+ + -anchor => "n", + -pady => 5, + ); + + $parent->MyText ( + -foreground => $EDIT_FOREGROUND, + -background => $EDIT_BACKGROUND, + -height => $rows, + -width => $cols, + -modified => \&$validate, + -text => $value, + )->pack ( + -side => "left", + -pady => 5, + -anchor => "s", + ); + } # _createText + + #--------------------------------------------------------------------------- + # _createButton (): Creates a pushbutton widget in $parent with a $label and + # an $action. + #--------------------------------------------------------------------------- + sub _createButton ($$$) { + my ($parent, $label, $action) = @_; + + $parent->Button ( + -activeforeground => $EDIT_FOREGROUND, + -activebackground => $EDIT_BACKGROUND, + -text => $label, + -width => length $label, + -command => \$action + )->pack ( + -side => "left", + -padx => 5 + ); + } # _createButton + + #--------------------------------------------------------------------------- + # _changeDropDown (): Refreshes the values in the dropdown menu. + #--------------------------------------------------------------------------- + sub _changeDropDown ($@) { + my ($dropdown, @values) = @_; + + if ($dropdown) { + my $menu = $dropdown->menu; + + if ($menu) { + $dropdown->menu->delete (0, "end"); + } # if + + $dropdown->addOptions (@values); + } # if + } # _changeDropDown + + #--------------------------------------------------------------------------- + # _refresh (): Refreshes the application by getting news values from + # Clearquest. Note a change in one dropdown may change others, + # so we re-get all of them through this procedure. + #--------------------------------------------------------------------------- + sub _refresh () { + my $fieldname; + + $fieldname = "category"; + @_categories = _getChoices $CQTool::entity, $fieldname; + $hd{$fieldname} = $_categories[0] if !$hd{$fieldname}; + $CQTool::entity->SetFieldValue ($fieldname, $hd{$fieldname}); + + $fieldname = "related_version"; + @_related_versions = _getChoices $CQTool::entity, $fieldname; + $hd{$fieldname} = $_related_versions[0] if !$hd{$fieldname}; + $CQTool::entity->SetFieldValue ($fieldname, $hd{$fieldname}); + + $fieldname = "platform"; + @_platforms = _getChoices $CQTool::entity, $fieldname; + $hd{$fieldname} = $_platforms[0] if !$hd{$fieldname}; + $CQTool::entity->SetFieldValue ($fieldname, $hd{$fieldname}); + + $fieldname = "requestedpriority"; + @_requested_priorities = _getChoices $CQTool::entity, $fieldname; + $hd{$fieldname} = $_requested_priorities[0] if !$hd{$fieldname}; + $CQTool::entity->SetFieldValue ($fieldname, $hd{$fieldname}); + + _changeDropDown $_category, @_categories; + _changeDropDown $_related_version, @_related_versions; + _changeDropDown $_platform, @_platforms; + _changeDropDown $_requestor_priority, @_requested_priorities; + + _setSubmitButton; + } # _refresh + + #--------------------------------------------------------------------------- + # _getNames (): Translates an array of badge numbers into a hash of names + # as the key and badge numbers as the value. + #--------------------------------------------------------------------------- + sub _getNames (@) { + my (@badges) = @_; + + my %names; + + foreach (@badges) { + my $query = $CQTool::session->BuildQuery ("users"); + + $query->BuildField ("fullname"); + + my $filter = $query->BuildFilterOperator ($CQPerlExt::CQ_BOOL_OP_AND); + + # Clearquest requires values to be in an array + my @badge = $_; + + $filter->BuildFilter ("login_name", $CQPerlExt::CQ_COMP_OP_EQ, \@badge); + + my $result = $CQTool::session->BuildResultSet ($query); + + $result->Execute; + + my $status = $result->MoveNext; + + my $fullname; + + while ($status == $CQPerlExt::CQ_SUCCESS) { + $fullname = $result->GetColumnValue (1); + $status = $result->MoveNext; + } # while + + $names{$fullname ? $fullname : ""} = $_; + } # foreach + + return %names; + } # _getNames + + #--------------------------------------------------------------------------- + # _darken (): Returns a slightly darker color than the passed in color + #--------------------------------------------------------------------------- + sub _darken ($) { + my ($color) = @_; + + # Get the RGB values + my ($r, $g, $b) = $_createHelpDeskUI->rgb($color); + + # Set them to $DARKEN % of their previous values + my $DARKEN = .8; + my $rhex = sprintf "%x", $r * $DARKEN; + my $ghex = sprintf "%x", $g * $DARKEN; + my $bhex = sprintf "%x", $b * $DARKEN; + + # Return a color string + return "\#$rhex$ghex$bhex"; + } # _darken + + #--------------------------------------------------------------------------- + # _createHelpDeskUI (): This is the main and exported routine that creates + # and handles the entire Perl/Tk application for + # creating a Help Desk ticket. + #--------------------------------------------------------------------------- + sub createHelpDeskUI () { + $_createHelpDeskUI = MainWindow->new; + + $EDIT_FOREGROUND = $_createHelpDeskUI->optionGet ("foreground", "Foreground"); + $EDIT_BACKGROUND = _darken ($_createHelpDeskUI->optionGet ("background", "Background")); + + $hd{id} = "None" if !$hd{id}; + + $_createHelpDeskUI->title ("Submit Helpdesk $hd{id}"); + + my $frame0 = $_createHelpDeskUI->Frame->pack (-pady => 2); + my $frame1 = $_createHelpDeskUI->Frame->pack; + my $frame2 = $_createHelpDeskUI->Frame->pack; + my $frame3 = $_createHelpDeskUI->Frame->pack; + my $frame4 = $_createHelpDeskUI->Frame->pack; + my $frame5 = $_createHelpDeskUI->Frame->pack; + my $frame6 = $_createHelpDeskUI->Frame->pack; + + _createTextField + $frame1, + "Headline", + \$hd{headline}, + 100, + \&_validateentry; + + _createText + $frame2, + "Description", + \$hd{description}, + 24, 100, + \&_validatetext; + + @_categories = _getChoices $CQTool::entity, "category"; + @_related_versions = _getChoices $CQTool::entity, "related_version"; + @_platforms = _getChoices $CQTool::entity, "platform"; + @_requested_priorities = _getChoices $CQTool::entity, "requestedpriority"; + @_requestors = _getChoices $CQTool::entity, "requestor"; + + my %requestor_names = _getNames @_requestors; + + @_requestors = (); + + foreach (sort keys %requestor_names) { + if ($_ eq "") { + push @_requestors, ""; + } else { + push @_requestors, "$_ ($requestor_names{$_})"; + } # if + } # foreach + + @_locations = _getChoices $CQTool::entity, "requestorlocation"; + + $_requestor = _createBrowseEntry + $frame3, + 0, 0, + "Requestor", + \&_refresh, + \$hd{requestor}, + @_requestors; + $_location = _createDropDown + $frame3, + 0, 3, + "Location", + \&_refresh, + \$hd{location}, + @_locations; + + $_category = _createDropDown + $frame4, + 0, 0, + "Category", + \&_refresh, + \$hd{category}, + @_categories; + $_related_version = _createDropDown + $frame4, + 0, 3, + "Related Version", + \&_refresh, + \$hd{related_version}, + @_related_versions; + + $_platform = _createDropDown + $frame5, + 0, 0, + "Platform", + \&_refresh, + \$hd{platform}, + @_platforms; + $_requestor_priority = _createDropDown + $frame5, + 0, 3, + "Requested Priority", + \&_refresh, + \$hd{requestedpriority}, + @_requested_priorities; + + $_submit = _createButton $frame6, "Submit", \&_submit; + + $_submit->configure ( + -state => "disabled", + ); + + _createButton $frame6, "Display", \&_displayValues if (get_debug); + _createButton $frame6, "About", \&_helpAbout; + _createButton $frame6, "Exit", sub { _destroyHelpDeskUI }; + } # createHelpDeskUI + +1; diff --git a/clients/GD/cqtool/CreateWORUI.pm b/clients/GD/cqtool/CreateWORUI.pm new file mode 100644 index 0000000..8a62a86 --- /dev/null +++ b/clients/GD/cqtool/CreateWORUI.pm @@ -0,0 +1,575 @@ +############################################################################## +# +# Name: CreateWORUI.pm +# +# Description: CreateWORUI.pm is a Perl module that encapsulates a +# Perl/Tk application to create a WOR. This application +# was developed for a few reasons. First ucmwb needs to +# be able to create WORs. The approach was to use +# IBM/Rational's cqtool +# (/opt/rational/clearquest/bin/cqtool) but there is two +# problems with this. First IBM/Rational's cqtool is +# unsupported and documented. Secondly IBM/Rational's +# cqtool is going away as of Clearquest 7.0. +# +# Another problem is that while IBM/Rational's cqtool +# would work, it does not return the ID of the WOR +# created! +# +# So this Perl/Tk module was created to create WORs. Perl +# interfaces with Clearquest to call the appropraite +# Clearquest action hooks and the like. Note that only +# the basic information is asked for. If you really want +# to create or modify a full WOR use Clearquest. This +# Perl/Tk app's main customer is ucmwb. +# +# Author: Andrew@ClearSCM.com +# +# (c) Copyright 2007, General Dynamics, all rights reserved +# +############################################################################## +use strict; +use warnings; + +package CreateWORUI; + use Tk; + use Tk::Dialog; + use Tk::MyText; + + use Display; + use CQTool; + + use base "Exporter"; + + my $ME = "CreateWOR"; + my $VERSION = "1.1"; + + # Colors + my ($EDIT_FOREGROUND, $EDIT_BACKGROUND); + + our %wor; + + our @EXPORT = qw ( + createWORUI + %wor + ); + + # Globals + my $_createWORUI; + + # Dropdowns + my ( + $_projects, + $_rclcs, + $_prod_arch1s, + $_prod_arch2s, + $_engr_targets, + $_work_codes, + $_work_products, + $_wor_classes, + ); + + # Choice lists + my ( + @_projects, + @_rclcs, + @_prod_arch1s, + @_prod_arch2s, + @_engr_targets, + @_work_codes, + @_work_products, + @_wor_classes, + ); + + # Buttons + my $_submit; + + ############################################################################ + # Subroutines + ############################################################################ + + #--------------------------------------------------------------------------- + # _helpAbout (): Puts up the Help: About dialog box + #--------------------------------------------------------------------------- + sub _helpAbout () { + my $text = "$ME v$VERSION\n"; + + $text .= < of ClearSCM, Inc. +END + + my $desc = $_createWORUI->Dialog ( + -title => "About $ME", + -text => $text, + -buttons => [ "OK" ], + ); + + $desc->Show (); + } # _helpAbout + + #--------------------------------------------------------------------------- + # _displayValues (): Displays the contents for %wor hash + #--------------------------------------------------------------------------- + sub _displayValues () { + foreach (keys %wor) { + if ($wor{$_}) { + display ("$_: $wor{$_}"); + } else { + display ("$_: undef"); + } # if + } # foreach + } # _displayValues + + #--------------------------------------------------------------------------- + # _getChoices (): For a given $entity and $fieldname, this routine returns + # the given choice list from Clearquest. + #--------------------------------------------------------------------------- + sub _getChoices ($$) { + my ($entity, $fieldname) = @_; + + return @{$entity->GetFieldChoiceList ($fieldname)}; + } # _getChoices + + #--------------------------------------------------------------------------- + # _destroyCreateWORUI (): Destroys the current WOR UI recycling Tk objects + #--------------------------------------------------------------------------- + sub _destroyCreateWORUI () { + # Destroy all globals created + destroy $_submit; + destroy $_projects; + destroy $_rclcs; + destroy $_prod_arch1s; + destroy $_prod_arch2s; + destroy $_engr_targets; + destroy $_work_codes; + destroy $_work_products; + destroy $_createWORUI; + + $_submit = + $_projects = + $_rclcs = + $_prod_arch1s = + $_prod_arch2s = + $_engr_targets = + $_work_codes = + $_work_products = + $_wor_classes = + $_createWORUI = undef; + + %wor = (); + } # _destroyCreateWORUI + + #--------------------------------------------------------------------------- + # _submit (): Actually creates the WOR given the filled out %wor hash. + #--------------------------------------------------------------------------- + sub _submit () { + debug "Creating WOR..."; + _displayValues if get_debug; + my $new_id = CQTool::submitWOR ($CQTool::entity, %wor); + + display ($new_id) if $new_id; + + _destroyCreateWORUI; + + return $new_id; + } # _submit + + #--------------------------------------------------------------------------- + # _setSubmitButton (): Sets the submit button to active only if all required + # fields have values. + #--------------------------------------------------------------------------- + sub _setSubmitButton (;$) { + my ($headline) = @_; + + return if !$_submit; + + # Check to see if we can activate the submit button + my $state = "normal"; + + foreach (@CQTool::wor_required_fields) { + if ($_ eq "headline") { + if (defined $headline) { + if ($headline eq "") { + $state = "disable"; + last; + } else { + next; + } # if + } # if + } # if + + if (!$wor{$_} or $wor{$_} eq "") { + $state = "disable"; + last; + } # if + } # foreach + + $_submit->configure ( + -state => $state, + ); + } # _setSubmitButton + + #--------------------------------------------------------------------------- + # _validateText (): Gets the text from the MyText widget and sets the submit + # button + #--------------------------------------------------------------------------- + sub _validateText { + my ($text) = @_; + + $wor{description} = $text->get_text; + chomp $wor{description}; + + _setSubmitButton $text; + + return 1; + } # _validateText + + #--------------------------------------------------------------------------- + # _validateEntry (): Gets the text from the headline widget and sets the + # submit button + #--------------------------------------------------------------------------- + sub _validateEntry { + my ($entry) = @_; + + _setSubmitButton $entry; + + return 1; + } # _validateEntry + + #--------------------------------------------------------------------------- + # _createDropDown (): Creates a dropdown widget in $parent in a grid at the + # $x, $y coordinates with a $label and a $value, using + # dropdown @values and a $refresh procedure. + #--------------------------------------------------------------------------- + sub _createDropDown ($$$$$$@) { + my ($parent, $x, $y, $label, $refresh, $value, @values) = @_; + + $parent->Label ( + -width => length $label, + -text => "$label:", + )->grid ( + -row => $x, + -column => $y, + -sticky => "e", + ); + + # Color the active foreground otherwise it's defaulted to ugly grey! + return $parent->Optionmenu ( + -activeforeground => $EDIT_FOREGROUND, + -activebackground => $EDIT_BACKGROUND, + -command => \&$refresh, + -variable => $value, + -options => \@values, + )->grid ( + -row => $x, + -column => $y + 1, + -sticky => "w", + ); + } # _createDropDown + + #--------------------------------------------------------------------------- + # _createTextField (): Creates a text field widget in $parent with a $label + # and a $value, using a $maxlen and a $validate + # procedure. + #--------------------------------------------------------------------------- + sub _createTextField ($$$$$) { + my ($parent, $label, $value, $maxlen, $validate) = @_; + + $parent->Label ( + -text => "$label:", + -justify => "right", + -width => 10, + )->pack ( + -side => "left", + -anchor => "e", + ); + + $parent->Entry ( + -foreground => $EDIT_FOREGROUND, + -background => $EDIT_BACKGROUND, + -width => $maxlen, + -justify => "left", + -textvariable => $value, + -validate => "key", + -validatecommand => \&$validate, + )->pack ( + -side => "left", + -padx => 5, + -anchor => "e", + ); + } # _createTextField + + #--------------------------------------------------------------------------- + # _createText (): Creates a multiline text field widget in $parent with a + # $label and a $value, using the specified $rows and $cols + # and a $validate procedure. + #--------------------------------------------------------------------------- + sub _createText ($$$$$$) { + my ($parent, $label, $value, $rows, $cols, $validate) = @_; + + $parent->Label ( + -text => "$label:", + -justify => "right", + -width => 10, + )->pack ( + -side => "left",+ + -anchor => "n", + -pady => 5, + ); + + $parent->MyText ( + -foreground => $EDIT_FOREGROUND, + -background => $EDIT_BACKGROUND, + -height => $rows, + -width => $cols, + -modified => \&$validate, + -text => $value, + )->pack ( + -side => "left", + -pady => 5, + -anchor => "s", + ); + } # _createText + + #--------------------------------------------------------------------------- + # _createButton (): Creates a pushbutton widget in $parent with a $label and + # an $action. + #--------------------------------------------------------------------------- + sub _createButton ($$$) { + my ($parent, $label, $action) = @_; + + $parent->Button ( + -activeforeground => $EDIT_FOREGROUND, + -activebackground => $EDIT_BACKGROUND, + -text => $label, + -width => length $label, + -command => \$action + )->pack ( + -side => "left", + -padx => 5 + ); + } # _createButton + + #--------------------------------------------------------------------------- + # _changeDropDown (): Refreshes the values in the dropdown menu. + #--------------------------------------------------------------------------- + sub _changeDropDown ($@) { + my ($dropdown, @values) = @_; + + if ($dropdown) { + my $menu = $dropdown->menu; + + if ($menu) { + $dropdown->menu->delete (0, "end"); + } # if + + $dropdown->addOptions (@values); + } # if + } # _changeDropDown + + #--------------------------------------------------------------------------- + # _refresh (): Refreshes the application by getting news values from + # Clearquest. Note a change in one dropdown may change others, + # so we re-get all of them through this procedure. + #--------------------------------------------------------------------------- + sub _refresh () { + my $fieldname; + + $fieldname = "project"; + my %projects = CQTool::getProjects $CQTool::session; + $wor{$fieldname} = $_projects[0] if !$wor{fieldname}; + $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname}); + + $fieldname = "prod_arch1"; + @_prod_arch1s = _getChoices $CQTool::entity, $fieldname; + $wor{$fieldname} = $_prod_arch1s[0] if !$wor{$fieldname}; + $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname}); + + $fieldname = "prod_arch2"; + @_prod_arch2s = _getChoices $CQTool::entity, $fieldname; + $wor{$fieldname} = $_prod_arch2s[0] if !$wor{$fieldname}; + $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname}); + + $fieldname = "rclc_name"; + @_rclcs = @{$projects{$wor{project}}}; + $wor{$fieldname} = $_rclcs[0] if !$wor{$fieldname}; + $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname}); + + $fieldname = "engr_target"; + @_engr_targets = _getChoices $CQTool::entity, $fieldname; + $wor{$fieldname} = $_engr_targets[0] if !$wor{$fieldname}; + $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname}); + + $fieldname = "work_code_name"; + @_work_codes = _getChoices $CQTool::entity, $fieldname; + $wor{$fieldname} = $_work_codes[0] if !$wor{$fieldname}; + $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname}); + + $fieldname = "work_product_name"; + @_work_products = _getChoices $CQTool::entity, $fieldname; + $wor{$fieldname} = $_work_products[0] if !$wor{$fieldname}; + $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname}); + + _changeDropDown ($_projects, keys %projects); + _changeDropDown ($_rclcs, @_rclcs); + _changeDropDown ($_prod_arch1s, @_prod_arch1s); + _changeDropDown ($_prod_arch2s, @_prod_arch2s); + _changeDropDown ($_engr_targets, @_engr_targets); + _changeDropDown ($_work_codes, @_work_codes); + _changeDropDown ($_work_products, @_work_products); + + _setSubmitButton (); + } # _refresh + + #--------------------------------------------------------------------------- + # _darken (): Returns a slightly darker color than the passed in color + #--------------------------------------------------------------------------- + sub _darken ($) { + my ($color) = @_; + + # Get the RGB values + my ($r, $g, $b) = $_createWORUI->rgb($color); + + # Set them to $DARKEN % of their previous values + my $DARKEN = .8; + my $rhex = sprintf "%x", $r * $DARKEN; + my $ghex = sprintf "%x", $g * $DARKEN; + my $bhex = sprintf "%x", $b * $DARKEN; + + # Return a color string + return "\#$rhex$ghex$bhex"; + } # _darken + + #--------------------------------------------------------------------------- + # createWORUI (): This is the main and exported routine that creates and + # handles the entire Perl/Tk application for creating a + # WOR. + #--------------------------------------------------------------------------- + sub createWORUI () { + $_createWORUI = MainWindow->new; + + $EDIT_FOREGROUND = $_createWORUI->optionGet ("foreground", "Foreground"); + $EDIT_BACKGROUND = _darken ($_createWORUI->optionGet ("background", "Background")); + + $wor{id} = "None" if !$wor{id}; + + $_createWORUI->title ("Submit WOR $wor{id}"); + + my $frame0 = $_createWORUI->Frame->pack (-pady => 2); + my $frame1 = $_createWORUI->Frame->pack; + my $frame2 = $_createWORUI->Frame->pack; + my $frame3 = $_createWORUI->Frame->pack; + my $frame4 = $_createWORUI->Frame->pack; + + _createTextField ( + $frame1, + "Headline", + \$wor{headline}, + 100, + \&_validateEntry + ); + + _createText ( + $frame2, + "Description", + \$wor{description}, + 24, 100, + \&_validateText + ); + + my %projects = CQTool::getProjects ($CQTool::session); + @_projects = keys %projects; + + $_projects = _createDropDown ( + $frame3, + 0, 0, + "Project", + \&_refresh, + \$wor{project}, + @_projects + ); + $_rclcs = _createDropDown ( + $frame3, + 0, 3, + "Revision Control Life Cycle", + \&_refresh, + \$wor{rclc_name}, + @_rclcs + ); + + $_prod_arch1s = _createDropDown ( + $frame3, + 2, 0, + "Product Architecture 1", + \&_refresh, + \$wor{prod_arch1}, + @_prod_arch1s + ); + $_engr_targets = _createDropDown ( + $frame3, + 2, 3, + "Engineering Target", + \&_refresh, + \$wor{engr_target}, + @_engr_targets + ); + + $_prod_arch2s = _createDropDown ( + $frame3, + 4, 0, + "Product Architecture 2", + \&_refresh, + \$wor{prod_arch2}, + @_prod_arch2s + ); + $_work_codes = _createDropDown ( + $frame3, + 4, 3, + "Work Code", + \&_refresh, + \$wor{work_code_name}, + @_work_codes + ); + + $_work_products = _createDropDown ( + $frame3, + 6, 0, + "Work Product", + \&_refresh, + \$wor{work_product_name}, + @_work_products + ); + + my $fieldname = "wor_class"; + @_wor_classes = _getChoices $CQTool::entity, $fieldname; + $wor{$fieldname} = "Worker"; + $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname}); + + $_wor_classes = _createDropDown ( + $frame3, + 6, 3, + "WOR Class", + sub {}, + \$wor{wor_class}, + @_wor_classes + ); + + # Default WOR Class to Worker + $_wor_classes->setOption ("Worker"); + + $_submit = _createButton ($frame4, "Submit", \&_submit); + + $_submit->configure ( + -state => "disabled", + ); + + _createButton ($frame4, "Display", \&_displayValues) if (get_debug); + _createButton ($frame4, "About", \&_helpAbout); + _createButton ($frame4, "Exit", \&_destroyCreateWORUI); + } # createWORUI + +1; diff --git a/clients/GD/cqtool/cqtool.pl b/clients/GD/cqtool/cqtool.pl new file mode 100755 index 0000000..3c85914 --- /dev/null +++ b/clients/GD/cqtool/cqtool.pl @@ -0,0 +1,780 @@ +#!/usr/bin/env /opt/rational/clearquest/bin/cqperl +############################################################################## +# +# Name: cqtool +# +# Description: cqtool is an interface to Clearquest to perform some simple +# actions to the RANCQ database. It is used primarily by ucmwb +# but it also supports a command line interface. +# +# The following commands are supported: +# +# activate : +# Activate WOR +# assign : +# Assign the WOR +# clone : +# Clones a WOR +# comment +# Add a comment to the Notes_Entry field for the WOR +# complete : +# Complete WOR +# createhd: +# Create a new Help Desk Ticket +# createwor: +# Create a new WOR +# effort : +# Update the WOR's actual hours +# exit|quit: +# Exits cqtool +# help: +# This display +# link : +# Link a parent WOR to a child WOR +# resolve : +# Resolve WOR +# set +# Set to for the +# usage: +# Displays command line usage +# version: +# Displays version of cqtool +# +# Many of these commands simply perform actions on a wor. Two +# of these commands, createwor and createhd have Perl/Tk GUI +# interfaces. +# +# Command line usage: +# +# Usage: cqtool\t[-usage|help] [-verbose] [-debug] +# [-userid ] [-password ] [] +# +# Where: +# +# -usage|help: Display usage +# -verbose: Turn on verbose mode +# -debug: Turn on debug mode +# -userid: User ID to log into Clearquest database as +# -password: Password to use +# If specified then cqtool executes and +# exits +# +# Environment: cqtool supports the following environment variables +# that are used mostly for tesing purposes +# +# CQ_DBSET: Clearquest DBSET to open (e.g. XTST3 for testing - +# default RANCQ) +# CQ_USER: User name to log into the $CQ_DBSET database with +# CQ_PASSWORD: Password to use to log into the $CQ_DBSET with. +# +# Author: Andrew@DeFaria.com +# +# (c) Copyright 2007, General Dynamics, all rights reserved +# +############################################################################## +use strict; +use warnings; + +use CQPerlExt; +use FindBin; +use Getopt::Long; +use Term::ANSIColor qw (:constants); + +use lib ("$FindBin::Bin", "$FindBin::Bin/../lib"); + +use SCCM::Misc; +use Display; +use CQTool; +use CreateWORUI; +use CreateHelpDeskUI; +use Logger; + +my $VERSION = BOLD GREEN . "1.1" . RESET; +my $PROMPT = BOLD YELLOW . ">>" . RESET; +my $UCMWB_PROMPT = ">>"; +my $DESC = BOLD RED . "$FindBin::Script" . + RESET " Version " . + $VERSION . + CYAN ": Program to talk to Clearquest" . + RESET; + +# Globals +my $_userid = $ENV{CQ_USER} ? $ENV{CQ_USER} : $ENV{USER}; +my $_password = $ENV{CQ_PASSWORD}; +my $_db_name = $ENV{CQ_DBSET} ? $ENV{CQ_DBSET} : "RANCQ"; +my $_ucmwb; + +my $_log; + +if (get_debug) { + $_log = new Logger ( + path => "/tmp", + append => 1, + ); +} # if + +my %_commands = ( + activate => \&activate, + assign => \&assign, + clone => \&clone, + comment => \&comment, + complete => \&complete, + createhd => \&createHelpDesk, + createwor => \&createWOR, + effort => \&effort, + exit => \&shutdown, + help => \&help, + link => \&linkParentWor2ChildWor, + quit => \&shutdown, + resolve => \&resolve, + set => \&set, + usage => \&usage, + version => \&announce, +); + +############################################################################## +# Forwards +############################################################################## +sub commandLoop (@); + +############################################################################## +# Main +############################################################################## +MAIN: { + GetOptions ( + "usage" => sub { usage () }, + "verbose" => sub { set_verbose () }, + "debug" => sub { set_debug () }, + "userid=s" => \$_userid, + "password=s" => \$_password, + "database=s" => \$_db_name, + "ucmwb" => \$_ucmwb, + ) || usage (); + + exit (commandLoop(@ARGV)); +} # MAIN + +############################################################################## +# Subroutines +############################################################################## + +#----------------------------------------------------------------------------- +# shutdown (): Ends program +#----------------------------------------------------------------------------- +sub shutdown () { + exit (0); +} # exit + +#----------------------------------------------------------------------------- +# help (): Displays help +#----------------------------------------------------------------------------- +sub help () { + display ($DESC); + display < : + Activate WOR +assign : + Assign the WOR +clone : + Clones a WOR +comment + Add a comment to the Notes_Entry field for the WOR +complete : + Complete WOR +createhd: + Create a new Help Desk Ticket +createwor: + Create a new WOR +effort : + Update the WOR's actual hours +exit|quit: + Exits $FindBin::Script +help: + This display +link : + Link a parent WOR to a child WOR +resolve : + Resolve WOR +set + Set to for the +usage: + Displays command line usage +version: + Displays version of $FindBin::Script +END +} # help + +#----------------------------------------------------------------------------- +# announce (): Announce ourselves +#----------------------------------------------------------------------------- +sub announce () { + display ($DESC); +} # Announce + +#----------------------------------------------------------------------------- +# dberror ($): Handle errors when talking to Clearquest. Note we need to reset +# the database connection if an error happens. +#----------------------------------------------------------------------------- +sub dberror ($) { + my ($msg) = @_; + + # Need to not only report the error but to reopen the + # database. Something gets corruppted if we don't! + error ($msg); + + closeDB (); + + openDB ($_userid, $_password, $_db_name); +} # DBError + +#----------------------------------------------------------------------------- +# getEntity ($$): Get an entity from Clearquest +#----------------------------------------------------------------------------- +sub getEntity ($$) { + my ($recordname, $wor) = @_; + + my $entity; + + eval { + $entity = $CQTool::session->GetEntity ($recordname, $wor); + }; + + if ($@) { + chomp $@; + dberror ($@); + return undef; + } else { + return $entity; + } # if +} # getEntity + +#----------------------------------------------------------------------------- +# set ($$$): Set $field to $value for $wor +#----------------------------------------------------------------------------- +sub set ($$@) { + my ($wor, $field, $value) = @_; + + if (!$wor or $wor eq "") { + error ("WOR is required"); + return 1; + } # if + + if (!$field or $field eq "") { + error ("Field is required"); + return 1; + } # if + + my $entity = getEntity ("WOR", $wor); + + return 1 if !$entity; + + $session->EditEntity ($entity, "modify"); + + $_log->msg ("Modifying $field to \"$value\"") if get_debug; + eval { + $entity->SetFieldValue ($field, $value); + }; + + if ($@) { + dberror ("$field set failed for WOR $wor:\n$@"); + return 2; + } # if + + my $status = $entity->Validate (); + + if ($status ne "") { + $entity->Revert (); + error ("$field validate failed for WOR $wor:\n$status"); + return 2; + } # if + + $status = $entity->Commit (); + + if ($status ne "") { + error ("$field update failed during Submit for $wor:\n$status"); + return 2; + } # if + + return 0; +} # set + +#----------------------------------------------------------------------------- +# clone ($): Clone a WOR +#----------------------------------------------------------------------------- +sub clone ($) { + my ($wor) = @_; + + if (!$wor) { + error ("WOR not specified!"); + return 1; + } # if + + $entity = getEntity ("WOR", $wor); + + return 1 if !$entity; + + # Check state + my $state = $entity->GetFieldValue ("state")->GetValue (); + + if ($state ne "Closed") { + error ("WOR $wor not closed - Unable to clone!"); + return 1; + } # if + + verbose ("Cloning WOR $wor..."); + + my $result = 0; + + eval { + # Currently Clone doesn't return a proper result but eventually... + $result = $CQTool::session->FireRecordScriptAlias ($entity, "Clone"); + }; + + if ($@) { + chomp $@; + dberror ($@); + return 1; + } # if + + return $result; +} # clone + +#----------------------------------------------------------------------------- +# effort ($$): Update actual hours for a WOR +#----------------------------------------------------------------------------- +sub effort ($$) { + my ($wor, $actualHrs) = @_; + + return set $wor, "ActualEffort", $actualHrs; +} # effort + +#----------------------------------------------------------------------------- +# comment (): Update the Notes_Entry comment field for a WOR +#----------------------------------------------------------------------------- +sub comment ($) { + my ($wor) = @_; + + if (!$wor) { + error "WOR not defined in call to comment!"; + return 1; + } # if + + if (!$_ucmwb) { + display ("Enter comments below. When finished, enter \".\" on a line by itself or hit ^D:"); + } else { + # We still need to prompt for the comments however signal UCMWB + # that command is ready for more input. + display_nolf ($UCMWB_PROMPT); + } # if + + my $comments; + + while () { + last if $_ eq ".\n"; + $comments .= $_; + } # while + + chomp $comments; + + $_log->msg ("Comments:\n$comments") if get_debug; + + return set $wor, "Note_Entry", $comments; +} # Comment + +#----------------------------------------------------------------------------- +# linkParentWor2ChildWor ($$): Link a child WOR to a parent WOR +#----------------------------------------------------------------------------- +sub linkParentWor2ChildWor ($$) { + my ($parentWor, $childWor) = @_; + + my $status; + + verbose ("Linking $parentWor -> $childWor..."); + + my $childentity = getEntity ("WOR", $childWor); + my $parententity = getEntity ("WOR", $parentWor); + + return 1 unless $childentity and $parententity; + + $session->EditEntity ($parententity, "modify"); + + $parententity->AddFieldValue ("wor_children", $childWor); + + $status = $parententity->Validate (); + + if ($status ne "") { + $parententity->Revert (); + error ("Validation failed while attempting to add child WOR $childWor to parent WOR $parentWor:\n$status"); + return 1; + } # if + + eval { + $status = $parententity->Commit (); + }; + + $status = $@ if $@; + + if ($status ne "") { + (error "Commit failed while trying to add child WOR $childWor to parent WOR $parentWor:\n$status"); + return 2; + } # if + + debug "Modifying child $childWor..."; + $session->EditEntity ($childentity, "modify"); + + $childentity->SetFieldValue ("wor_parent", $parentWor); + + $status = $childentity->Validate (); + + if ($status ne "") { + $childentity->Revert (); + error "Validation failed while attempting to add parent WOR $parentWor to child WOR $childWor:\n$status"; + return 1; + } # if + + eval { + $status = $childentity->Commit (); + }; + + $status = $@ if $@; + + if ($status ne "") { + error "Commit failed while trying to add parent WOR $parentWor to child WOR $childWor:\n$status"; + return 2; + } # if + + return 0; +} # linkParentWor2ChildWor + +#----------------------------------------------------------------------------- +# assign ($$$$): Assign a WOR +#----------------------------------------------------------------------------- +sub assign ($$$$$) { + my ($wor, $assignee, $project, $plannedHrs, $startDate) = @_; + + if (!$wor or $wor eq "") { + error ("WOR is required"); + return 1; + } # if + + if (!$assignee or $assignee eq "") { + error ("Assignee must be specified"); + return 1; + } # if + + if (!$project or $project eq "") { + error ("UCM Project is required"); + return 1; + } # if + + if (!$startDate or $startDate eq "") { + error ("Planned Start Date is required"); + return 1; + } # if + + my $entity = getEntity ("WOR", $wor); + + return 1 if !$entity; + + my $state = $entity->GetFieldValue ("state")->GetValue (); + + if ($state ne "Submitted") { + error ("WOR $wor is not in Submitted state!\nState: $state"); + return 2; + } # if + + $session->EditEntity ($entity, "assign"); + + $entity->SetFieldValue ("ucm_project", $project) if $project ne ""; + $entity->SetFieldValue ("PlannedStart", $startDate) if $startDate ne ""; + $entity->SetFieldValue ("PlannedEffort", $plannedHrs) if $plannedHrs ne ""; + $entity->SetFieldValue ("Owner", $assignee) if $assignee ne ""; + + my $status = $entity->Validate (); + + if ($status ne "") { + $entity->Revert (); + error ("Assign failed for WOR $wor:\n$status"); + return 2; + } # if + + $status = $entity->Commit (); + + if ($status ne "") { + error ("Assign failed during Submit for WOR $wor:\n$status"); + return 2; + } # if + + return 0; +} # assign + +#----------------------------------------------------------------------------- +# activate (): Activate a WOR +#----------------------------------------------------------------------------- +sub activate ($$$$$) { + my ($wor, $project, $estHrs, $startDate, $endDate) = @_; + + if (!$wor or $wor eq "") { + error ("WOR is required"); + return 1; + } # if + + if (!$project or $project eq "") { + error ("UCM Project is required"); + return 1; + } # if + + if (!$startDate or $startDate eq "") { + error ("Planned Start Date is required"); + return 1; + } # if + + if (!$endDate or $endDate eq "") { + error ("Planned End Date is required"); + return 1; + } # if + + my $entity = getEntity ("WOR", $wor); + + return 1 if !$entity; + + my $state = $entity->GetFieldValue ("state")->GetValue (); + + if ($state ne "Assessing") { + error ("WOR $wor is not in Assessing state!\nstate: $state"); + return 2; + } # if + + $session->EditEntity ($entity, "activate"); + + $entity->SetFieldValue ("ucm_project", $project) if $project ne ""; + $entity->SetFieldValue ("EstimatedEffort", $estHrs) if $estHrs ne ""; + $entity->SetFieldValue ("PlannedStart", $startDate) if $startDate ne ""; + $entity->SetFieldValue ("PlannedEnd", $endDate) if $endDate ne ""; + + my $status = $entity->Validate (); + + if ($status ne "") { + $entity->Revert (); + error ("Activate failed for WOR $wor:\n$status"); + return 2; + } # if + + $status = $entity->Commit (); + + if ($status ne "") { + error ("Activate failed during Submit for WOR $wor:\n$status"); + return 2; + } # if + + return 0; +} # activate + +#----------------------------------------------------------------------------- +# resolve ($): Resolve a WOR +#----------------------------------------------------------------------------- +sub resolve ($) { + my ($wor) = @_; + + if (!$wor or $wor eq "") { + error ("WOR is required"); + return 1; + } # if + + my $entity = getEntity ("WOR", $wor); + + return 1 if !$entity; + + my $state = $entity->GetFieldValue ("state")->GetValue (); + + if ($state ne "Working") { + error ("WOR $wor is not in Working state!\nState: $state"); + return 2; + } # if + + $session->EditEntity ($entity, "resolve"); + + my $status = $entity->Validate (); + + if ($status ne "") { + $entity->Revert (); + error ("Resolve failed for WOR $wor:\n$status"); + return 2; + } # if + + $status = $entity->Commit (); + + if ($status ne "") { + error ("Resolve failed during Submit for WOR $wor:\n$status"); + return 2; + } # if + + return 0; +} # resolve + +#----------------------------------------------------------------------------- +# complete ($$): Complete a WOR +#----------------------------------------------------------------------------- +sub complete ($$) { + my ($wor, $actualHrs) = @_; + + if (!$wor or $wor eq "") { + error ("WOR is required"); + return 1; + } # if + + if (!$wor or $wor eq "") { + error ("Actual Hours are required"); + return 1; + } # if + + my $entity = getEntity ("WOR", $wor); + + return 1 if !$entity; + + my $state = $entity->GetFieldValue ("state")->GetValue (); + + if ($state ne "Verifying") { + error ("WOR $wor is not in Verifying state!\nState:$state"); + return 2; + } # if + + $session->EditEntity ($entity, "complete"); + $entity->SetFieldValue ("ActualEffort", $actualHrs) if $actualHrs ne ""; + + my $status = $entity->Validate (); + + if ($status ne "") { + $entity->Revert (); + error ("Complete failed for WOR $wor:\n$status"); + return 2; + } # if + + $status = $entity->Commit (); + + if ($status ne "") { + error ("Complete failed during Submit for WOR $wor:\n$status"); + return 2; + } # if + + return 0; +} # Complete + +#----------------------------------------------------------------------------- +# executeCommand (@): Executes a cqtool command +#----------------------------------------------------------------------------- +sub executeCommand (@) { + my (@args) = @_; + + my $cmd = lc shift @args; + + return if $cmd eq ""; + + if ($_commands{$cmd}) { + if (!$CQTool::session) { + if ( # Commands that do not require a database connection + !($cmd eq "exit" or + $cmd eq "quit" or + $cmd eq "help" or + $cmd eq "usage" or + $cmd eq "verbose")) { + verbose "Opening $_db_name as $_userid..."; + + if (!$_password) { + display_nolf ("${_userid}'s password:"); + `stty -echo`; + $_password = ; + chomp $_password; + display (""); + `stty echo`; + } # if + + openDB ($_userid, $_password, $_db_name); + } # if + } # if + + # Treat args: Args that are enclosed in quotes must be + # combined. For simplicity's sake we will only support matched + # pairs of double quotes. Anything else results in undefined + # behavior. + my (@new_args); + + foreach (@args) { + # Quoted argument starting + if (/^\"(.*)\"$/s) { + push @new_args, $1; + } else { + push @new_args, $_; + } # if + } # foreach + + $_log->msg ("$cmd (" . join (",", @new_args) . ")") if get_debug; + + return $_commands{$cmd} (@new_args); + } else { + error ("Unknown command \"$cmd\" (try help)"); + return 1; + } # if +} # executeCommand + +#----------------------------------------------------------------------------- +# commandLoop (@): This is the interactive command loop +#----------------------------------------------------------------------------- +sub commandLoop (@) { + my (@args) = @_; + + # For single, command line, commands... + return executeCommand (@args) if @args; + + announce if !$_ucmwb; + + while () { + if (!$_ucmwb) { + display_nolf ($PROMPT . RESET . UNDERLINE); + } else { + display_nolf ($UCMWB_PROMPT); + } # if + + # Read command into $_ + $_ = ; + chomp; + + # If we are not being called by ucmwb, display RESET to stop the + # UNDERLINE we were using. This keeps the output from being + # underlined. In ucmwb mode we are not using any of the terminal + # sequences. + display_nolf (RESET) if !$_ucmwb; + + # If the user hit Control-d then a ^D is displayed but we remain + # on the same line. So output a carriage return and exit 0. + if (!$_) { + display (""); + exit 0; + } # if + + # Special handling for set command since we want to take + # everything after to be a value, and we may get long + # values that are space separated and space significant + # (e.g. description?) + if (/^\s*(\w+)\s+(\w+)\s+(\w+)\s+(.*)/) { + if (lc $1 eq "set") { + my $cmd = $1; + my $wor = $2; + my $field = $3; + my $value = $4; + + # Change "\n"'s back to \n's + $value =~ s/\\n/\n/g; + + executeCommand ($cmd, $wor, $field, "\"$value\""); + } else { + executeCommand (split); + } # if + } else { + executeCommand (split); + } # if + } # while +} # commandLoop diff --git a/clients/GD/rexec b/clients/GD/rexec new file mode 100644 index 0000000..cd35cc4 --- /dev/null +++ b/clients/GD/rexec @@ -0,0 +1,329 @@ +#!/usr/local/bin/perl +################################################################################ +# +# File: $RCSfile: $ +# Revision: $Revision: $ +# Description: Remotely run processes on other machines +# Author: Andrew@DeFaria.com +# Created: Tue Jan 8 15:57:27 MST 2008 +# Modified: $Date: $ +# Language: perl +# +# (c) Copyright 2008, ClearSCM, Inc., all rights reserved +# +################################################################################ +use strict; +use warnings; + +use FindBin; +use Getopt::Long; +use Term::ANSIColor qw(:constants); +use POSIX ":sys_wait_h"; + +my $libs; + +BEGIN { + $libs = $ENV{SITE_PERLLIB} ? $ENV{SITE_PERLLIB} : "$FindBin::Bin/../lib"; + + die "Unable to find libraries\n" if !$libs and !-d $libs; +} + +use lib "$FindBin::Bin/../lib"; +use lib $libs; + +use Display; +use Logger; +use Machines; +use Rexec; +use Utils; + +our $_host; +our $_skip = 0; +our $_currentHost; + +my $_log = 0; +my $_quiet = 0; +my $_alternateFile; +my $_parallel = 0; + +my $_totalMachines = 0; +my $_totalExecutions = 0; +my $_totalFailures = 0; +my $_totalConnectFailures = 0; +my $_totalSkips = 0; + +my (%_workerStatuses, %_workerNames); + +sub Usage { + my $msg = shift; + + display "ERROR: $msg\n" if defined $msg; + + display "rexec\t[-v] [-d] [-u] "; + display "\t-v\tTurn on verbose mode"; + display "\t-d\tTurn on debug mode"; + display "\t-u\tThis usage message"; + display "\tCommand to execute remotely"; + + exit 1; +} # Usage + +sub printStats { + display YELLOW . "Machines: " . RESET . "$_totalMachines " . + MAGENTA . "Executions/Failures: " . RESET . "($_totalExecutions/$_totalFailures) " . + BLUE . "Connect Failures/Skips: " . RESET . "($_totalConnectFailures/$_totalSkips)"; +} # printStats + +sub Interrupted { + use Term::ReadKey; + + display BLUE . "\nInterrupted execution on $_host" . RESET; + + printStats; + + display_nolf "Executing on " . YELLOW . $_host . RESET . " - " + . GREEN . BOLD . "S" . RESET . GREEN . "kip" . RESET . ", " + . CYAN . BOLD . "C" . RESET . CYAN . "ontinue" . RESET . " or " + . MAGENTA . BOLD . "A" . RESET . MAGENTA . "bort run" . RESET . " (" + . GREEN . BOLD . "s" . RESET . "/" + . CYAN . BOLD . "C" . RESET . "/" + . MAGENTA . BOLD . "a" . RESET . ")?"; + + ReadMode ("cbreak"); + my $answer = ReadKey (0); + ReadMode ("normal"); + + if ($answer eq "\n") { + display "c"; + } else { + display $answer; + } # if + + $answer = lc $answer; + + if ($answer eq "s") { + *STDOUT->flush; + display "Skipping $_host"; + $_skip = 1; + $_totalSkips++; + } elsif ($answer eq "a") { + display RED . "Aborting run". RESET; + printStats; + exit; + } else { + display "Continuing..."; + $_skip = 0; + } # if +} # Interrupted + +sub workerDeath { + while ((my $worker = waitpid (-1, WNOHANG)) > 0) { + my $status = $?; + + # Ignore all child deaths except for processes we started + next if !exists $_workerStatuses{$worker}; + + $_workerStatuses{$worker} = $status; + } # while + + $SIG{CHLD} = \&workerDeath; +} # workerDeath + +sub execute ($$$) { + my ($cmd, $host, $prompt) = @_; + + my @lines; + + verbose_nolf "Connecting to machine $host..."; + + eval { + $_currentHost = new Rexec ( + host => $host, + prompt => $prompt, + ); + }; + + # Problem with creating Rexec object. Log error if logging and return. + if ($@ or !$_currentHost) { + if ($_log) { + my $log = new Logger (name => $_host); + + $log->err ("Unable to connect to $host to execute command\n$cmd"); + } # if + + $_totalConnectFailures++; + + return (1, ()); + } # if + + verbose " connected"; + + display YELLOW . "$host:" . RESET . UNDERLINE . "$cmd" . RESET unless $_quiet; + + @lines = $_currentHost->exec ($cmd); + + if ($_skip) { + # Kick current connection + kill INT => $_currentHost->{handle}->pid; + } # if + + if ($_parallel != 0) { + if ($_log) { + my $log = new Logger (name => $_host); + + $log->err ("Unable to connect to $host to execute command\n$cmd"); + } # if + + $_totalConnectFailures++; + } # if + + verbose "Disconnected from $host"; + + my $status = $_currentHost->status; + + undef $_currentHost; + + return ($status, @lines); +} # execute + +sub parallelize ($%) { + my ($cmd, %machines) = @_; + + my $thread_count = 1; + + foreach $_host (sort keys %machines) { + if ($thread_count <= $_parallel) { + debug "Processing $_host ($thread_count)"; + $thread_count++; + + if (my $pid = fork) { + # In parent process - record this host and its status + $_workerNames{$pid} = $_host; + } else { + # In spawned child... + $pid = $$; + + debug "Starting process for $_host [$pid]"; + + $_workerNames{$pid} = $_host; + + my ($status, @lines) = execute $cmd, $_host, $machines{$_host}; + + my $log = new Logger (name => $_host); + + $log->log ($_) foreach (@lines); + + exit $status; + } # if + } else { + # Wait for somebody to finish; + debug "Waiting for somebody to exit..."; + my $reaped = wait; + + debug "Reaped $_workerNames{$reaped} [$reaped] (Status: $?)"; + $_workerStatuses{$reaped} = $? >> 8 if $reaped != -1; + + $thread_count--; + } # if + } # foreach + + # Wait for all kids + my %threads = %_workerNames; + + foreach (keys %threads) { + if (waitpid ($_, 0) == -1) { + delete $threads{$_}; + } else { + $_workerStatuses{$_} = $? >> 8; + debug "$threads{$_} [$_] exited with a status of $_workerStatuses{$_}"; + } # if + } # foreach + + debug "All processed completed - Status:"; + + if (get_debug) { + foreach (sort keys %_workerStatuses) { + debug "$_workerNames{$_}\t[$_]:\tStatus: $_workerStatuses{$_}"; + } # foreach + } # if + + # Gather output... + display "Output of all executions"; + foreach $_host (sort keys %machines) { + if (-f "$_host.log") { + display "$_host:$_" foreach (ReadFile ("$_host.log")); + + #unlink "$_host.log"; + } else { + warning "Unable to find output for $_host ($_host.log missing)"; + } # if + } # foreach +} # parallelize + +# Print the totals if interrupted +$SIG{INT} = \&Interrupted; + +# Get our options +GetOptions ( + "usage" => sub { Usage "" }, + "verbose" => sub { set_verbose }, + "debug" => sub { set_debug }, + "log" => \$_log, + "quiet" => \$_quiet, + "file=s" => \$_alternateFile, + "parallel:i" => \$_parallel, +) || Usage "Unknown parameter"; + +my $cmd = join " ", @ARGV; + +error "No command specified", 1 if !$cmd; + +my $machines = Machines->new (file => $_alternateFile); +my %machines = $machines->all (); + +if ($_parallel > 0) { + parallelize ($cmd, %machines); + printStats; + exit; +} # if + +display "NOTE: Logging output to .log" if $_log; + +foreach $_host (sort keys (%machines)) { + $_totalMachines++; + + my ($status, @lines) = execute $cmd, $_host, $machines{$_host}; + + if ($_skip) { + $_skip = 0; + next; + } # if + + if (defined $status) { + if ($status == 0) { + $_totalExecutions++; + } else { + if ($_log) { + my $log = new Logger (name => $_host); + + $log->log ("Host: $_host\nCommand: $cmd\nStatus: $status\nOutput:\n"); + $log->log ($_) foreach (@lines); + } # if + + $_totalFailures++; + + next; + } # if + } # if + + if ($_log) { + my $log = new Logger (name => $_host); + + $log->log ("Host: $_host\nCommand: $cmd\nStatus: $status\nOutput:\n"); + $log->log ($_) foreach (@lines); + } else { + display $_ foreach (@lines); + } # if +} # foreach + +printStats; diff --git a/clients/HP/bin/SCS b/clients/HP/bin/SCS new file mode 100644 index 0000000..55dfe36 --- /dev/null +++ b/clients/HP/bin/SCS @@ -0,0 +1,878 @@ +#!/bin/sh +############################################# +# @(#)scs 1.45 +# system characterization script +# "scs" collects data from Atria +# customer sites for analysis by +# engineering. +###################################### + +######################### +# For Solaris 5.x systems +######################### +nddit() +{ +for i in tcp udp ip hme +do + /usr/sbin/ndd /dev/$i \? | /bin/awk '{print $1}' | \ + /bin/egrep -v '\?|directed|respond|status|hash' > /tmp/$$ + for j in `/bin/cat /tmp/$$` + do + /bin/printf "%-30s %s\n" $j `/usr/sbin/ndd /dev/$i $j ` + done + /bin/rm /tmp/$$ + echo "--------" +done +} + +solaris() +{ +show "Uptime" /bin/uptime +show "System Device Configuration" /usr/sbin/prtconf -vP +show "System Configuration" /usr/sbin/sysdef -i +show "System Customization" '/bin/cat /etc/system | \ + /bin/grep -v "^\*" | /bin/grep -v "^$"' +show "Network Configuration" nddit +show "System Messages" /bin/dmesg +show "Patches" /bin/showrev -p +# /usr/kvm/prtconf check and use +#show "fpversion" /opt/SUNWspro/bin/fpversion + +show "Processes" '/usr/ucb/ps auxww | /bin/fold -80' +show "Network Configuration" /usr/sbin/ifconfig -a +show "Network Utilization Summary" /bin/netstat -i +show "Network Protocol Statistics" /bin/netstat -s +show "Network Mbuf Statistics" /bin/netstat -m +show "VM Statistics" /bin/vmstat -s +show "Cache Flush Statistics" /bin/vmstat -c +show "Interrupts" /bin/vmstat -i +show "NFS Statistics" /bin/nfsstat +show "NFS Responses" /bin/nfsstat -m + +show "CPU Utilization" /bin/sar -u 1 10 +show "Buffer Activity" /bin/sar -b 1 10 +show "Block Device Activity" /bin/sar -d 1 10 +show "Disk Device Activity" iostat -x 1 10 +show "Paging In Activity" /bin/sar -p 1 5 +show "Paging Out Activity" /bin/sar -g 1 5 +show "Free Memory" /bin/sar -r 1 5 +show "vmstat snapshot" /bin/vmstat 2 10 +show "Kernel Memory Allocation Activity" /bin/sar -k 1 5 +show "KMA statistics" 'echo kmastat | /usr/sbin/crash' +echo "" +show "Swap space" /usr/sbin/swap -s +echo "" +} + +####################### +# For SUNOS 4.x systems +####################### +sunos() +{ + +show "Memory " '/etc/dmesg | grep mem' +show "System Messages" /etc/dmesg +show "Processes" '/bin/ps auxww | /usr/ucb/fold -80' +show "Network Utilization Summary" /usr/ucb/netstat -i +show "Network Protocol Statistics" /usr/ucb/netstat -s +show "Network Mbuf Statistics" /usr/ucb/netstat -m +show "VM Statistics" /usr/ucb/vmstat -s +show "Cache Flush Statistics" /usr/ucb/vmstat -c +show "Interrupts" /usr/ucb/vmstat -i +show "NFS Statistics" /usr/etc/nfsstat + +show "Disk Device Activity" /bin/iostat -D 1 10 +show "CPU Activity" /usr/ucb/vmstat 1 10 +show "Tables" /etc/pstat -T +show "vmstat snapshot" /usr/ucb/vmstat 2 10 + +} + + +####################### +# For AIX +####################### +aix() +{ + +show "Processes" '/usr/bin/ps -elf ' +show "Network Utilization Summary" /bin/netstat -i +show "Network Protocol Statistics" /bin/netstat -s +show "Network Mbuf Statistics" /bin/netstat -m +show "Network Configurables" /usr/sbin/no -a +show "NFS Statistics" /usr/sbin/nfsstat + +show "System Attributes" /usr/sbin/lsattr -E -l sys0 +show "CPU Utilization" sar -u 1 10 +show "Buffer Activity" sar -b 1 10 +show "Disk Activity" /bin/iostat 1 10 +show "Paging Stats" sar -r 1 5 +show "Network Monitor for 30s" '/bin/netpmon -v -o /tmp/$$; sleep 30; trcstop' +echo "" +} + +####################### +# For OSF1 +####################### +osf1() +{ +show "Boot Messages" 'echo "";echo ""; echo "";uerf -r 300 | tail -100' +show "Processes" '/usr/bin/ps glww | fold -80' +show "Network Utilization Summary" /usr/sbin/netstat -i +show "Network Protocol Statistics" /usr/sbin/netstat -s +show "Network Mbuf Statistics" /usr/sbin/netstat -m +show "NFS Statistics" /usr/bin/nfsstat +show "Kernel memory usage" /usr/bin/vmstat -M +show "vmstat snapshopt" /usr/bin/vmstat 2 10 +show "Disk Device snapshot" /bin/iostat 2 10 + +} + +####################### +# For IRIX 6.x +####################### +irix6() +{ +show "Uptime" /usr/bsd/uptime +show "System Device Configuration" /usr/sbin/sysconf +show "System Hardware Configuration" /usr/bin/hinv +show "System Software Configuration" /etc/chkconfig +show "Patches" '/usr/sbin/versions | grep Patch' +show "Disk Usage" /usr/sbin/df -l -k +show "System Customization" '/bin/cat /var/sysgen/stune | \ + /bin/grep -v "^\*" | /bin/grep -v "^$"' + +show "Processes" '/usr/bin/ps -elf | /bin/fold -80' +show "Network Utilization Summary" /usr/etc/netstat -i +show "Network Protocol Statistics" /usr/etc/netstat -s +show "Network Mbuf Statistics" /usr/etc/netstat -m +show "NFS Statistics" /usr/etc/nfsstat + +show "CPU Utilization" sar -u 1 10 +show "Buffer Activity" sar -b 1 10 +show "Block Device Activity" sar -d 1 10 +show "Paging In Activity" sar -p 1 5 +show "Paging Out Activity" sar -g 1 5 +show "Free Memory" sar -r 1 5 +echo "" +} + + +####################### +# For IRIX 5.x +####################### +irix() +{ +show "Uptime" /usr/bsd/uptime +show "System Device Configuration" /usr/sbin/sysconf +show "System Hardware Configuration" /usr/bin/hinv +show "System Software Configuration" /etc/chkconfig +show "Disk Usage" /usr/sbin/df -l -k +show "System Customization" '/bin/cat /var/sysgen/stune | \ + /bin/grep -v "^\*" | /bin/grep -v "^$"' + +show "Processes" '/usr/bin/ps -elf | /bin/fold -80' +show "Network Utilization Summary" /usr/etc/netstat -i +show "Network Protocol Statistics" /usr/etc/netstat -s +show "Network Mbuf Statistics" /usr/etc/netstat -m +show "NFS Statistics" /usr/etc/nfsstat + +show "CPU Utilization" sar -u 1 10 +show "Buffer Activity" sar -b 1 10 +show "Block Device Activity" sar -d 1 10 +show "Paging In Activity" sar -p 1 5 +show "Paging Out Activity" sar -g 1 5 +show "Free Memory" sar -r 1 5 +echo "" +} + + +##################### +# For HP-UX 9/800 systems +##################### +hpux98() +{ +show "Memory " grep "mem =" /usr/adm/*syslog +#first the shared info +hpux +# now the system specific +show "System definition" /etc/sysdef +show "CPU Utilization" /usr/bin/sar -u 1 10 +show "Buffer Activity" /usr/bin/sar -b 1 10 +show "Block Device Activity" /usr/bin/sar -d 1 10 +} +##################### +# For HP-UX 9/700 systems +##################### +hpux97() +{ +show "Memory " grep Physical /usr/adm/messages +# just the shared info +hpux +} +##################### +# For All HP-UX 9 systems +##################### +hpux() +{ +show "Uptime" /usr/bin/uptime +show "System Messages" /etc/dmesg +show "Swap Space" /etc/swapinfo +show "Patches" ls -ld /system/PH* +show "IO devices" /etc/ioscan -f + +show "Processes" '/bin/ps -elf | /usr/bin/fold -80' +show "Network Utilization Summary" /usr/bin/netstat -i +show "Network Protocol Statistics" /usr/bin/netstat -s +show "Network Mbuf Statistics" /usr/bin/netstat -m +show "VM Statistics" /usr/bin/vmstat -s +show "NFS Statistics" /usr/etc/nfsstat +show "Network errors" /etc/netfmt -t 30 -f /usr/adm/nettl.LOG00 + +show "Disk Utilization" /usr/bin/bdf -l +show "vmstat snapshot" /usr/bin/vmstat 2 10 +} + +##################### +# For HP-UX 10 systems +##################### +hpux10() +{ + +show "Memory " grep Physical /var/adm/syslog/syslog.log +show "System DMessages" /usr/sbin/dmesg +show "System definition" /usr/sbin/sysdef +show "Messages" "tail -100 /usr/adm/syslog/syslog.log" +show "IO status" /etc/ioscan -f +show "Patches" '/usr/sbin/swlist -l product | grep PH' + +show "Processes" '/usr/bin/ps -elf | /usr/bin/fold -80' +show "Network Utilization Summary" /usr/bin/netstat -i +show "Network Protocol Statistics" /usr/bin/netstat -s +show "Network Mbuf Statistics" /usr/bin/netstat -m +show "VM Statistics" /usr/bin/vmstat -s +show "NFS Statistics" /usr/bin/nfsstat + +show "CPU Utilization" /usr/bin/sar -u 1 10 +show "Buffer Activity" /usr/bin/sar -b 1 10 +show "Block Device Activity" /usr/bin/sar -d 1 10 +show "vmstat snapshot" /usr/bin/vmstat 2 10 +} +##################### +# For Unixware systems +##################### +unixware() +{ +show "Memory size" /sbin/memsize +show "Message log" cat /usr/adm/log/osmlog +show "System Configuration" /usr/sbin/sysdef +show "Processes" '/usr/bin/ps -elf ' +show "Network Utilization Summary" /bin/netstat -i +show "Network Protocol Statistics" /bin/netstat -s +show "NFS Statistics" /usr/sbin/nfsstat + +show "CPU Utilization" /sbin/sar -u 1 10 +show "Buffer Activity" /sbin/sar -b 1 10 +show "Block Device Activity" /sbin/sar -d 1 10 +show "Paging In Activity" /sbin/sar -p 1 5 +show "Paging Out Activity" /sbin/sar -g 1 5 +show "Free Memory" /sbin/sar -r 1 5 +show "Historical sar data" /sbin/sar -A +echo "" +} + + +#################################### +# display informationin a uniform way +#################################### +show() { + echo "----------------------------------------------------" + echo $1 + echo "----------------------------------------------------" + shift + eval "$@" + echo "" +} +################################################## +# check uw network - works for Unixware +################################################## +check_uw_network() { + +netstat -i $interface +sleep 10 +netstat -i $interface +} + +################################################## +# check network - works for HP-UX and SUNOS 5.x +################################################## +check_network() { + printf "%8s %8s %8s %8s %8s %8s \n" time inpkts inerrs \ + outpkts outerrs colls + netstat -i -I $interface $interval | ( + (line ; line ; line) > /dev/null + t_inpkts=0 # initialize counter + t_inerrs=0 # initialize counter + t_outpkts=0 # initialize counter + t_outerrs=0 # initialize counter + t_colls=0 # initialize counter + i=0 + while test $i -lt $count ; do # for each of the lines + time=`date +%T` + /bin/echo $time \\c + set -- `line` # get the line + printf "%8s %8s %8s %8s %8s\n" $1 $2 $3 $4 $5 + t_inpkts=`expr $1 + $t_inpkts` # accumulate in packets + shift + t_inerrs=`expr $1 + $t_inerrs` # accumulate in errors + shift + t_outpkts=`expr $1 + $t_outpkts` # accumulate out packets + shift + t_outerrs=`expr $1 + $t_outerrs` # accumulate out errors + shift + t_colls=`expr $1 + $t_colls` # accumulate collisions + i=`expr $i + 1 ` + done + printf "\n%8s %8s %8s %8s %8s %8s \n" \ + total $t_inpkts $t_inerrs $t_outpkts $t_outerrs $t_colls +# now check error and collision rate. +# Use awk to get floating point accuracy + echo $t_colls $t_outpkts $t_inerrs $t_inpkts | awk '$2 != 0 { + collision_rate = $1 / $2; + printf("\n\ncollision rate ( %g %% )", (collision_rate * 100.0)); + if ( collision_rate > 0.05 ) + printf(" too high. Add subnets.\n"); + else + printf("\n")} + $4 != 0 { + error_rate = $3 / $4; + printf(" error rate ( %g %% )", (error_rate * 100.0)); + if (error_rate > 0.00025) + printf(" too high. Check cabling.\n"); + else + printf("\n")}' + + ) +} + +################################################## +# check fddi - for sun +################################################## +check_fddi_sol() { +/opt/*conn/*nf/utilities/nf_stat $interface 3 5 +/opt/*conn/*nf/utilities/nf_stat -m 3 5 +/opt/*fddi/fddistat -l +} + +################################################## +# check fddi - for HPs +################################################## +check_fddi_hp() { +/usr/bin/fddistat /dev/$interface +} + +################################################## +# check network - works for SUNOS 4.x +################################################## +check_network1() { + echo " time inpkts inerrs outpkts outerrs colls" + netstat -i -I $interface $interval | ( + (line ; line ; line) > /dev/null + t_inpkts=0 # initialize counter + t_inerrs=0 # initialize counter + t_outpkts=0 # initialize counter + t_outerrs=0 # initialize counter + t_colls=0 # initialize counter + i=0 + while test $i -lt $count ; do # for each of the lines + time=`date +%T` + /bin/echo -n $time + set -- `line` # get the line + echo " $1 $2 $3 $4 $5" + t_inpkts=`expr $1 + $t_inpkts` # accumulate in packets + shift + t_inerrs=`expr $1 + $t_inerrs` # accumulate in errors + shift + t_outpkts=`expr $1 + $t_outpkts` # accumulate out packets + shift + t_outerrs=`expr $1 + $t_outerrs` # accumulate out errors + shift + t_colls=`expr $1 + $t_colls` # accumulate collisions + i=`expr $i + 1 ` + done + echo -n "total " + echo -n "$t_inpkts $t_inerrs $t_outpkts " + echo " $t_outerrs $t_colls" +# now check error and collision rate. +# Use awk to get floating point accuracy + echo $t_colls $t_outpkts $t_inerrs $t_inpkts | awk '$2 != 0 { + collision_rate = $1 / $2; + printf("\n\ncollision rate ( %g %% )", (collision_rate * 100.0)); + if ( collision_rate > 0.05 ) + printf(" - too many collisions. \n"); + else + printf("\n")} + $4 != 0 { + error_rate = $3 / $4; + printf(" error rate ( %g %% )", (error_rate * 100.0)); + if (error_rate > 0.00025) + printf(" - too many errors. \n"); + else + printf("\n")}' + + ) +} + +######################################################## +# get disk layout and performance data +######################################################## +do_ssaadm() +{ +#need to add logic here to select a configured controller +#and only do an ssaadm if relevant. We'll live with the errors +# for now.... +for i in `(cd /dev/rdsk; ls | cut -d't' -f 1 | uniq)` +do + ssaadm display $i 2>/dev/null + ssaadm display -p $i 2>/dev/null +done +} + +######################################################## +# attempt to get Sparc Storage Array configuration +######################################################## +do_arrays() +{ +SSAS=`/usr/sbin/prtconf -vP | grep soc | grep instance | wc -l` +show "SSAs" echo "$SSAS SparcStorage Arrays attached" + +if [ $SSAS = 0 ] ; then + return +fi + +#else lets print out information + +show "SSA Disks and Performance" do_ssaadm +show "State and Configuration of Array Disks" /usr/sbin/vxprint -ht +show "Disk utilization" /usr/sbin/vxstat -i 2 -c 5 +show "Disk utilization" /usr/sbin/vxstat -i 2 -c 5 -s + +} + +######################################################## +# establish file partition -> disk device mapping for solaris +# how do I do this for other platforms? +######################################################## +do_discs() +{ +df -F ufs -k +show "Disk Device Mapping" echo ' ' +df -F ufs | cut -f1 -d: | awk '{print $2}' | sed 's/(//' |\ + sed 's/)//' > /tmp/lll +for i in `cat /tmp/lll` +do + ls -l $i +done +rm /tmp/lll + +show "path_to_inst file" cat /etc/path_to_inst +} + +######################################################## +# calculate the size (in bytes) of all VOB database on this host +######################################################## +check_vobs() +{ +if [ ! -f $ATRIAHOME/bin/cleartool ] ; then + echo this is not a ClearCase host + exit +fi +# list all vobs on this host and extract the +# VOB storage directory. +$ATRIAHOME/bin/cleartool lsvob -host $HOST +$ATRIAHOME/bin/cleartool lsvob -host $HOST | \ +awk '$1 == "*" {print $3 } \ + $1 != "*" {print $2 }' >/tmp/list.$$ +# count the vobs +vobs=`wc -l /tmp/list.$$ | awk '{print $1}'` +echo "Number of vobs : $vobs" +if [ $vobs = 0 ] ; then + return +fi +# now count all the bytes in the database data and key files +for i in `cat /tmp/list.$$` +do + if [ ! -d $i/db ] ; then + echo no db subdirectory for $i + continue + fi + cd $i/db + $LSL vob_db.d0? vob_db.k0? | \ + awk 'BEGIN {sum=0} \ + {sum = sum + $5/(1024.0*1024.0)} \ + END {printf " %8.3f Mb\t", sum}' + echo `basename $i` +done | tee -a /tmp/list1.$$ + +awk 'BEGIN {sum=0} \ +{sum = sum + $1} \ +END {printf "%9.3f Mb\tTOTAL", sum}' < /tmp/list1.$$ + +rm /tmp/list.$$ /tmp/list1.$$ +} + +######################################################## +# check the view characteristics +######################################################## +check_views() +{ +if [ ! -f $ATRIAHOME/bin/cleartool ] ; then + echo this is not a ClearCase host + exit +fi +$ATRIAHOME/bin/cleartool lsview -host $HOST | \ +awk '$1 == "*" {print $3,$2 } \ + $1 != "*" {print $2,$1 }' >/tmp/list.$$ +# count the views +views=`wc -l /tmp/list.$$ | awk '{print $1}'` +echo "Number of views : $views" +if [ $views = 0 ] ; then + return +fi +cat /tmp/list.$$ | ( +i=1 +while [ $i -le $views ] +do + set -- `line`; + size=`grep -s cache $1/.view | awk '{print $2}'` + if [ a$size = a ]; then + size=default + fi + echo "$size $2" | awk '{printf "%8s\t%-16s\n", $1, $2}' + i=`expr $i + 1` +done +) +rm /tmp/list.$$ +} + +######################################################## +# check the MVFS cache on this system +######################################################## +check_mvfs() +{ +/usr/atria/etc/mvfsstat -iclrh 2>&1 +} + +######################################################## +# obtain lockmgr parameters +######################################################## +get_lockmgr() +{ +if [ -f $ATRIAHOME/etc/init.d/atria ] ; then + grep lockmgr $ATRIAHOME/etc/init.d/atria | egrep '\-u' | \ + sed 's/.*}//' | sed 's/>>.*//' +elif [ -f $ATRIAHOME/etc/rc.atria ] ; then + grep lockmgr $ATRIAHOME/etc/rc.atria | egrep '\-u' | \ + sed 's/.*}//' | sed 's/>>.*//' +#for V3 +elif [ -f $ATRIAHOME/etc/atria_start ] ; then + grep lockmgr $ATRIAHOME/etc/atria_start | egrep '\-u' | \ + sed 's/.*}//' | sed 's/>>.*//' +fi +} + +######################################################## +# obtain lockmgr parameters +######################################################## +get_vob_counts() +{ +VSL=$VAR/adm/atria/log/vob_scrubber_log +if [ ! -f $VSL ] ; then + echo no data available +else + egrep 'Started|element|branch|version|derived|hyperlink' $VSL +fi + +} + +######################################################## +# check clearcase things +######################################################## +check_cc() +{ +ATRIAHOME=${ATRIAHOME:-/usr/atria} + show "VOB sizes" check_vobs + show "Views" check_views + show "MVFS" check_mvfs + show "Cleartool Version" $ATRIAHOME/bin/cleartool -ver + show "Lock Manager Configuration" get_lockmgr + show "VOB Characteristics" get_vob_counts + show "License Host" cat $VAR/adm/atria/config/license_host + show "Registry Host and Region" cat $VAR/adm/atria/rgy/rgy*.conf +} + +######################################################## +# obtain mvs parameters +######################################################## + +get_mvfs_sun4() +{ +adb -k /var/adm/atria/vmunix_mvfs /dev/mem </dev/null </dev/null < -fullname " + print -u2 "\t-employeetype -employeenumber +" + print -u2 "\t-manager -mailserver " + exit 1 +} # usage + +function email_postmaster { + notify="postmaster pdl-support" + mailx -s "Please setup email for $fullname" $notify < $message_file +} # email_postmaster + +message_file=$tmp_prefix.msg.$$ +username= +fullname= +employeetype= +employeenumber= +manager= +mailserver= + +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -username) + if [ $# -le 1 ]; then + error "Username not specified!" + usage + fi + shift + username="$1" + ;; + + -fullname) + if [ $# -le 1 ]; then + error "Full name not specified!" + usage + fi + shift + fullname="$1" + ;; + + -employeetype) + if [ $# -le 1 ]; then + error "Employee type not specified!" + usage + fi + shift + employeetype="$1" + + case "$employeetype" in + Employee|SEED|Contractor) + ;; + *) + error "Employeetype must be one of \"Employee\", \"SEED\" or \"Contractor\"" + exit 1 + ;; + esac + ;; + + -employeenumber) + if [ $# -gt 1 ]; then + shift + employeenumber="$1" + fi + ;; + + -manager) + if [ $# -le 1 ]; then + error "Manager name not specified!" + usage + fi + shift + manager="$1" + ;; + + -mailserver) + if [ $# -le 1 ]; then + error "Mail server not specified!" + usage + fi + shift + mailserver="$1" + ;; + + *) + error "Unknown parameter encounter: \"$1\"" + usage + ;; + esac + shift +done + +if [ "_$username" = "_" -o \ + "_$fullname" = "_" -o \ + "_$employeetype" = "_" -o \ + "_$manager" = "_" -o \ + "_$mailserver" = "_" ]; then + error "Missing parameter" + usage +fi + +case "$employeetype" in + Contractor) + if [ "_$employeenumber" != "_" ]; then + error "Contractors should not have an HP Employee number" + exit 1 + fi + ;; + *) # already verified that employeetype is correct + if [ "_$employeenumber" = "_" ]; then + error "Employee number is required for HP Employees and SEEDs" + exit 1 + fi + ;; +esac + +firstname=$(print $fullname | awk '{print $1}') + +cat > $message_file <> $message_file <> $message_file < -fullname " + print -u2 "\t-phone -homeserver -shell " + exit 1 +} # usage + +function add_to_moa { + cd $admin_root/lib + co -q -l $master_passwd + + if [ $? -ne 0 ]; then + error "Unable to checkout $master_passwd" + exit $? + fi + + trap cancel_checkout INT ERR + + if [ "$shell" = "tcsh" ]; then + shell="/app/tcsh" + else + shell="/bin/$shell" + fi + + uid=$(/app/newuid) # generate unique uid + print "$username:*:$uid:$gid:$fullname,42U,$phone,_MoA_:/nfs/$homeserver/data/home/$username:$shell" >> $master_passwd + + if [ $? -ne 0 ]; then + error "Unable to add entry to $master_passwd" + exit $? + fi + + ci -u -q -m"Added $fullname" $master_passwd + if [ $? -ne 0 ]; then + error "Unable to check in new master password file!" + exit $? + fi + + trap INT ERR + + cd $OLDPWD +} # add_to_moa + +function cancel_checkout { + info "Canceling checkout" + rcs -q -u $master_passwd + chmod -w $master_passwd + co -q $master_passwd + exit 1 +} # cancel_checkout +A +function user_exists { + grep -ve "^#" $master_passwd | cut -f1 -d: | + grep "$username" >/dev/null 2>&1 + return $? +} # user_exists + +# Find admin root +if [ -d /net/bismol/app/admin ]; then + admin_root=/net/bismol/app/admin +elif [ -d /net/hpclbis/app/admin ]; then + admin_root=/net/hpclbis/app/admin +elif [ -d /nfs/bismol/app/admin ]; then + admin_root=/nfs/bismol/app/admin +elif [ -d /nfs/hpclbis/app/admin ]; then + admin_root=/nfs/hpclbis/app/admin +elif [ -d /nfs/hpclbis/root/app/admin ]; then + admin_root=/nfs/hpclbis/root/app/admin +else + error "Internal error: Unable to ascertain admin_root!" + exit 1 +fi + +master_passwd=$admin_root/lib/master_passwd +gid=191 # lang group +username= +fullname= +phone= +homeserver= +shell= + +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -username) + if [ $# -le 1 ]; then + error "Username not specified!" + usage + fi + shift + username="$1" + ;; + + -fullname) + if [ $# -le 1 ]; then + error "Full name not specified!" + usage + fi + shift + fullname="$1" + ;; + + -phone) + if [ $# -le 1 ]; then + error "Phone not specified!" + usage + fi + shift + phone="$1" + ;; + + -homeserver) + if [ $# -le 1 ]; then + error "Home machine not specified!" + usage + fi + shift + homeserver="$1" + ;; + + -shell) + if [ $# -le 1 ]; then + error "Shell not specified!" + usage + fi + shift + shell="$1" + ;; + + *) + error "Unknown parameter encounter: \"$1\"" + usage + ;; + esac + shift +done + +if [ "_$username" = "_" -o \ + "_$fullname" = "_" -o \ + "_$phone" = "_" -o \ + "_$homeserver" = "_" -o \ + "_$shell" = "_" ]; then + error "Missing parameter" + usage +fi + +#if $(user_exists); then + #error "$username already exists in the master password file" +#else + add_to_moa + if [ $? -eq 0 ]; then + info "Account for $fullname has been successfully created" + else + error "Problems encountered trying to create account for $fullname" + fi +#fi diff --git a/clients/HP/bin/add_postnote b/clients/HP/bin/add_postnote new file mode 100644 index 0000000..483231e --- /dev/null +++ b/clients/HP/bin/add_postnote @@ -0,0 +1,182 @@ +#!/bin/ksh +################################################################################ +# +# File: add_postnote +# RCS: $Header: add_postnote,v 1.1 97/05/27 15:35:32 defaria Exp $ +# Description: This script adds a new person to the postnote addressbook +# Author: Andrew DeFaria, California Language Labs +# Created: Mon May 19 15:56:06 PDT 1997 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved. +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +function usage { + print -u2 "Usage: $me -username -fullname " + print -u2 "\t-phone -hostname " + print -u2 "\t-displayname " + exit 1 +} # usage + +function add_to_postnote { + cd $postnote_dir + check_out_file=$postnote_addressbook + co -q -l $check_out_file + + if [ $? -ne 0 ]; then + error "Unable to checkout $check_out_file" + exit $? + fi + + trap cancel_checkout INT ERR + + print "S:$fullname = Phone: $phonenumber = +($hostname,$displayname:0,$username@cup.hp.com,F,$xterm)" >> $check_out_file + + if [ $? -ne 0 ]; then + error "Unable to add entry to $check_out_file" + exit $? + fi + + ci -u -q -m"Added $fullname" $check_out_file + if [ $? -ne 0 ]; then + error "Unable to check in $check_out_file!" + exit $? + fi + + trap INT ERR + + cd $OLDPWD +} # add_to_postnote + +function cancel_checkout { + info "Canceling checkout" + rcs -q -u $check_out_file + chmod -w $check_out_file + co -q $check_out_file + exit 1 +} # cancel_checkout + +# Find AppServer's data directory +if [ -d /net/bismol/app/data ]; then + appserver_data=/net/bismol/app/data +elif [ -d /net/hpclbis/app/data ]; then + appserver_data=/net/hpclbis/app/data +elif [ -d /nfs/bismol/app/data ]; then + appserver_data=/nfs/bismol/app/data +elif [ -d /nfs/hpclbis/app/data ]; then + appserver_data=/nfs/hpclbis/app/data +elif [ -d /nfs/hpclbis/root/app/data ]; then + appserver_data=/nfs/hpclbis/root/app/data +else + error "Internal error: Unable to ascertain appserver_data!" + exit 1 +fi + +postnote_dir=$appserver_data +postnote_addressbook=$postnote_dir/pn_addressbook +username= +fullname= +phonenumber="????" +hostname= +displayserver= +xterm= +check_out_file= + +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -username) + if [ $# -le 1 ]; then + error "Username not specified!" + usage + fi + shift + username="$1" + ;; + + -fullname) + if [ $# -le 1 ]; then + error "Full name not specified!" + usage + fi + shift + fullname="$1" + ;; + -phone) + if [ $# -le 1 ]; then + error "Phone not specified!" + usage + fi + shift + phonenumber="$1" + ;; + + -hostname) + if [ $# -le 1 ]; then + error "Hostname not specified!" + usage + fi + shift + hostname="$1" + ;; + + -displayname) + if [ $# -le 1 ]; then + error "Displayname not specified!" + usage + fi + shift + displayname="$1" + ;; + + *) + error "Unknown parameter encounter: \"$1\"" + usage + ;; + esac + shift +done + +if [ "_$username" = "_" -o \ + "_$fullname" = "_" -o \ + "_$hostname" = "_" ]; then + error "Missing parameter" + usage +fi + +if [ "_$displayname" = "_" ]; then + displayname=$hostname:0.0 +elif [ "$displayname" != "$hostname" ]; then + xterm="T" +else + xterm="F" +fi + +add_to_postnote + +if [ $? -eq 0 ]; then + info "$fullname has been added to PostNote addressbook" + if [ "$xterm" = "T" ]; then + info "X Terminal Server: $hostname; X Terminal Display Name: +$displayname" + fi +else + error "Problems encountered trying to create PostNote entry for $fullname" +fi diff --git a/clients/HP/bin/add_sharedx b/clients/HP/bin/add_sharedx new file mode 100644 index 0000000..b8d6f9b --- /dev/null +++ b/clients/HP/bin/add_sharedx @@ -0,0 +1,187 @@ +#!/bin/ksh +################################################################################ +# +# File: add_sharedx +# RCS: $Header: add_sharedx,v 1.1 97/05/27 15:35:33 defaria Exp $ +# Description: This script adds a new person to the SharedX addressbook +# Author: Andrew DeFaria, California Language Labs +# Created: Mon May 19 15:56:06 PDT 1997 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +function usage { + print -u2 "Usage: $me -username -fullname " + print -u2 "\t-phone -hostname " + print -u2 "\t-displayname " + exit 1 +} # usage + +function add_to_sharedx { + cd $sharedx_dir + check_out_file=$sharedx_addressbook + co -q -l $check_out_file + + if [ $? -ne 0 ]; then + error "Unable to checkout $check_out_file" + exit $? + fi + + trap cancel_checkout INT ERR + + if [ "$xterm" = "T" ]; then + print "$displayname:0\t$fullname\t$phonenumber $username\t$hostname" >> +$check_out_file + else + print "$displayname:0\t$fullname\t$phonenumber $username" >> +$check_out_file + fi + + if [ $? -ne 0 ]; then + error "Unable to add entry to $check_out_file" + exit $? + fi + + ci -u -q -m"Added $fullname" $check_out_file + if [ $? -ne 0 ]; then + error "Unable to check in $check_out_file!" + exit $? + fi + + trap INT ERR + + cd $OLDPWD +} # add_to_sharedx + +function cancel_checkout { + info "Canceling checkout" + rcs -q -u $check_out_file + chmod -w $check_out_file + co -q $check_out_file + exit 1 +} # cancel_checkout + +# Find AppServer's data directory +if [ -d /net/bismol/app/data ]; then + appserver_data=/net/bismol/app/data +elif [ -d /net/hpclbis/app/data ]; then + appserver_data=/net/hpclbis/app/data +elif [ -d /nfs/bismol/app/data ]; then + appserver_data=/nfs/bismol/app/data +elif [ -d /nfs/hpclbis/app/data ]; then + appserver_data=/nfs/hpclbis/app/data +elif [ -d /nfs/hpclbis/root/app/data ]; then + appserver_data=/nfs/hpclbis/root/app/data +else + error "Internal error: Unable to ascertain appserver_data!" + exit 1 +fi + +sharedx_dir=$appserver_data/SharedX/address_books +sharedx_addressbook=$sharedx_dir/CLL +username= +fullname= +phonenumber="????" +hostname= +displayserver= +xterm= +check_out_file= + +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -username) + if [ $# -le 1 ]; then + error "Username not specified!" + usage + fi + shift + username="$1" + ;; + + -fullname) + if [ $# -le 1 ]; then + error "Full name not specified!" + usage + fi + shift + fullname="$1" + ;; + -phone) + if [ $# -le 1 ]; then + error "Phone not specified!" + usage + fi + shift + phonenumber="$1" + ;; + + -hostname) + if [ $# -le 1 ]; then + error "Hostname not specified!" + usage + fi + shift + hostname="$1" + ;; + + -displayname) + if [ $# -le 1 ]; then + error "Displayname not specified!" + usage + fi + shift + displayname="$1" + ;; + + *) + error "Unknown parameter encounter: \"$1\"" + usage + ;; + esac + shift +done + +if [ "_$username" = "_" -o \ + "_$fullname" = "_" -o \ + "_$displayname" = "_" ]; then + error "Missing parameter" + usage +fi + +if [ "_$hostname" = "_" ]; then + hostname=$displayname +elif [ "$displayname" != "$hostname" ]; then + xterm="T" +else + xterm="F" +fi + +add_to_sharedx + +if [ $? -eq 0 ]; then + info "$fullname has been added to Shared/X addressbook" + if [ "$xterm" = "T" ]; then + info "X Terminal Server: $hostname; X Terminal Display Name: +$displayname" + fi +else + error "Problems encountered trying to create Shared/X entry for $fullname" +fi diff --git a/clients/HP/bin/add_synchronize b/clients/HP/bin/add_synchronize new file mode 100644 index 0000000..803a92d --- /dev/null +++ b/clients/HP/bin/add_synchronize @@ -0,0 +1,195 @@ +#!/bin/ksh +################################################################################ +# +# File: add_synchronize +# RCS: $Header: add_synchronize,v 1.2 97/05/27 15:35:51 defaria Exp +$ +# Description: This script adds a new person to synchronize +# Author: Andrew DeFaria, California Language Labs +# Created: Mon May 19 15:56:06 PDT 1997 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +function usage { + print -u2 "Usage: $me -username -fullname " + print -u2 "\t-groupname " + exit 1 +} # usage + +function add_to_synchronize { + cd $synchro_db + check_out_file=$synchro_users + co -q -l $check_out_file + + if [ $? -ne 0 ]; then + error "Unable to checkout $check_out_file" + exit $? + fi + + trap cancel_checkout INT ERR + + print "$fullname,\t$username,\t$username@cup.hp.com" >> $check_out_file + + if [ $? -ne 0 ]; then + error "Unable to add entry to $check_out_file" + exit $? + fi + + ci -u -q -m"Added $fullname" $check_out_file + if [ $? -ne 0 ]; then + error "Unable to check in $check_out_file!" + exit $? + fi + + trap INT ERR + + cd $OLDPWD +} # add_to_synchronize + +function add_to_synchronize_group { + cd $synchro_db/GroupTemplates + check_out_file=$groupname + co -q -l $check_out_file + + if [ $? -ne 0 ]; then + error "Unable to checkout $check_out_file" + exit $? + fi + + trap cancel_checkout INT ERR + + print "$fullname" >> $check_out_file + + if [ $? -ne 0 ]; then + error "Unable to add entry to $check_out_file" + exit $? + fi + + ci -u -q -m"Added $fullname to $check_out_file" $check_out_file + if [ $? -ne 0 ]; then + error "Unable to check in $check_out_file!" + exit $? + fi + + trap INT ERR + + make > make.out 2>&1 + + if [ $? -ne 0 ]; then + error "Rebuilding of Synchronize groups failed" + exit $? + fi + + cd $OLDPWD +} # add_to_synchronize_group + +function cancel_checkout { + info "Canceling checkout" + rcs -q -u $check_out_file + chmod -w $check_out_file + co -q $check_out_file + exit 1 +} # cancel_checkout + +function user_exists { + grep -ve "^#" $synchro_users | cut -f1 -d',' | + grep "^$username$" >/dev/null 2>&1 + return $? +} # user_exists + +# Find synchro_dir +if [ -d /net/cllapp/opt/synchronize ]; then + synchro_dir=/net/cllapp/opt/synchronize +else + error "Internal error: Unable to ascertain synchro_dir!" + exit 1 +fi + +synchro_db=$synchro_dir/db +synchro_users=$synchro_db/users +username= +fullname= +groupname= +check_out_file= + +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -username) + if [ $# -le 1 ]; then + error "Username not specified!" + usage + fi + shift + username="$1" + ;; + + -fullname) + if [ $# -le 1 ]; then + error "Full name not specified!" + usage + fi + shift + fullname="$1" + ;; + -groupname) + if [ $# -le 1 ]; then + error "Groupname not specified!" + usage + fi + shift + groupname="$1" + ;; + + *) + error "Unknown parameter encounter: \"$1\"" + usage + ;; + esac + shift +done + +if [ "_$username" = "_" -o \ + "_$fullname" = "_" -o \ + "_$groupname" = "_" ]; then + error "Missing parameter" + usage +fi + +if $(user_exists); then + error "$username already exists in the Synchronize database" +elif [ ! -f $synchro_db/GroupTemplates/$groupname ]; then + error "Unknown Synchronize group $groupname" +else + add_to_synchronize + if [ $? -eq 0 ]; then + info "$fullname has been added as a Synchronize user" + else + error "Problems encountered trying to create Synchronize user for +$fullname" + fi + add_to_synchronize_group + if [ $? -eq 0 ]; then + info "$fullname has been successfully added to $groupname" + else + error "Problems encountered trying to add $fullname to $groupname" + fi +fi diff --git a/clients/HP/bin/add_user b/clients/HP/bin/add_user new file mode 100644 index 0000000..f9c2e9e --- /dev/null +++ b/clients/HP/bin/add_user @@ -0,0 +1,497 @@ +#!/bin/ksh +################################################################################ +# +# File: add_user +# Description: This script adds a user +# Author: Andrew DeFaria +# Language: Korn Shell +# Modified: +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Parameters +employeenumber= +employeetype= +fullname= +groupname= +manager= +phone= +username= +workstation= +shell= +hostname= +homeserver= +displayname= + +# fieldvalue is used when prompting for non-supplied fields +fieldvalue= + +# Logfile +logfile=$TMPDIR/add_user.$$.log + +## Set global env variables +# Set me +me=${0##*/} + +function error { + print -u2 "$me: Error: $1" +} # error + +function warning { + print -u2 "$me: Warning: $1" +} # warning + +function display { + print "$1" +} # display + +function info { + display "$me: Info: $1" +} # info + +function verbose { + if [ ! -z "$verbose" ]; then + display "$1" + fi +} # verbose + +function debug { + if [ ! -z "$debug" ]; then + print -u2 "$me: Debug: $1" + fi +} # debug + +function usage { + display "$me [-v|verbose] [-d|debug] [-usage]" + display " -v|verbose: Turns on verbose mode" + display " -d|debug: Turns on debug mode" + display " -usage: Print this usage message" + display + display "The following options will be prompted for if not supplied on the" + display "command line. If any command line parameter has spaces in it then" + display "you need to surround it in quotes (e.g. -owners_fullname" + display "\"Andrew DeFaria\". Note: Do NOT use quotes when responding to" + display "prompts for missing information." + display + display " -employeenumber Specify the Employee \#" + display " -employeetype One of Employee, SEED or Contractor" + display " -fullname The employee's full name" + display " -groupname Synchronize group name" + display " -manager Full name of manager" + display " -phone In the format of 7-XXXX (the t-44 will" + display " be prepended)" + display " -username Unix/NT username for this new user" + display " -workstation One of Unix|X Terminal|Win NT" + display " -shell One of ksh|sh|csh|tcsh" + display " -hostname Name of workstation host or X Terminal" + display " server" + display " -homeserver Name of machine where \$HOME will be" + display " created" + display " -displayname Name of DISPLAY" + + error "$1" + exit 1 +} # usage + +function prompt_for_field { + fieldname="$1" + fieldvalue= + + while [ ! -n "$fieldvalue" ]; do + display "Enter the value for $fieldname:\c" + read fieldvalue + + if [ ! -n "$fieldvalue" ]; then + error "Must specify $fieldname!" + fi + done +} # prompt_for_field + +function display_parms { + display "New user:" + display "------------------------------------------------------" + display "employeenumber = $employeenumber" + display "employeetype = $employeetype" + display "fullname = $fullname" + display "groupname = $groupname" + display "manager = $manager" + display "phone = $phone" + display "username = $username" + display "workstation = $workstation" + display "shell = $shell" + display "hostname = $hostname" + display "homeserver = $homeserver" + display "displayname = $displayname" + display + display "Command line equivalent:" + display + display "$me -employeenumber $employeenumber \\" + display " -employeetype $employeetype \\" + display " -fullname \"$fullname\" \\" + display " -groupname $groupname \\" + display " -manager \"$manager\" \\" + display " -phone $phone \\" + display " -username $username \\" + display " -workstation $workstation \\" + display " -shell $shell \\" + display " -hostname $hostname \\" + display " -homeserver $homeserver \\" + display " -displayname $displayname" + display "Are the parameters correct [Y|n]?\c" + read answer + case "$answer" in + Y|y) + : OK! + ;; + *) + exit + esac +} # display_parms + +# Get parameters +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -v|-verbose) + verbose=yes + ;; + + -d|-debug) + debug=yes + ;; + + -employeenumber) + if [ $# -gt 1 ]; then + shift + employeenumber="$1" + fi + ;; + + -employeetype) + if [ $# -gt 1 ]; then + shift + employeetype="$1" + fi + ;; + + -fullname) + if [ $# -gt 1 ]; then + shift + fullname="$1" + fi + ;; + + -groupname) + if [ $# -gt 1 ]; then + shift + groupname="$1" + fi + ;; + + -manager) + if [ $# -gt 1 ]; then + shift + manager="$1" + fi + ;; + + -phone) + if [ $# -gt 1 ]; then + shift + phone="$1" + fi + ;; + + -username) + if [ $# -gt 1 ]; then + shift + username="$1" + fi + ;; + + -workstation) + if [ $# -gt 1 ]; then + shift + workstation="$1" + fi + ;; + + -shell) + if [ $# -gt 1 ]; then + shift + shell="$1" + fi + ;; + + -hostname) + if [ $# -gt 1 ]; then + shift + hostname="$1" + fi + ;; + + -homeserver) + if [ $# -gt 1 ]; then + shift + homeserver="$1" + fi + ;; + + -displayname) + if [ $# -gt 1 ]; then + shift + displayname="$1" + fi + ;; + + *) + usage "Unrecognized parameter $1" + ;; + esac + shift +done + +if [ "_$employeenumber" = "_" ]; then + verbose "Employee Number was not specified!" + prompt_for_field "Employee Number" + employeenumber="$fieldvalue" +fi + +if [ "_$employeetype" = "_" ]; then + verbose "Employee Type was not specified!" + prompt_for_field "Employee Type" + employeetype="$fieldvalue" +fi + +while true; do + case "$employeetype" in + Employee|SEED|Contractor) + break + ;; + + *) + error "Employee Type was not one of \"Employee\", \"SEED\" or \"Contractor\"!" + prompt_for_field "Employee Type" + employeetype="$fieldvalue" + ;; + esac +done + +if [ "_$fullname" = "_" ]; then + verbose "Employee Name was not specified!" + prompt_for_field "Employee Name" + fullname="$fieldvalue" +fi + +if [ "_$groupname" = "_" ]; then + verbose "Project Name was not specified!" + prompt_for_field "Project Name" + groupname="$fieldvalue" +fi + +while true; do + if [ -f "/net/cllapp/opt/synchronize/db/GroupTemplates/$groupname" ]; + then + break + else + verbose "Project name \"$groupname\" is not valid!" + display + display "Valid Project names are:" + cd /net/cllapp/opt/synchronize/db/groups/Projects + ls * + cd $OLDPWD + display + prompt_for_field "Project Name" + groupname="$fieldvalue" + fi +done + +if [ "_$manager" = "_" ]; then + verbose "Project Manager was not specified!" + prompt_for_field "Project Manager" + manager="$fieldvalue" +fi + +if [ "_$phone" = "_" ]; then + verbose "Phone was not specified!" + prompt_for_field "Phone" + phone="$fieldvalue" +fi + +if [ "_$username" = "_" ]; then + verbose "Username was not specified!" + prompt_for_field "Username" + username="$fieldvalue" +fi + +if [ "_$workstation" = "_" ]; then + verbose "Workstation was not specified!" + prompt_for_field "Workstation" + workstation="$fieldvalue" +fi + +while true; do + case "$workstation" in + Unix|"X Terminal"|"Win NT") + break + ;; + + *) + error "Workstation was not one of Unix|X Terminal|Win NT!" + prompt_for_field "Workstation" + workstation="$fieldvalue" + ;; + esac +done + +if [ "_$shell" = "_" ]; then + verbose "Shell was not specified!" + prompt_for_field "Shell" + shell="$fieldvalue" +fi + +while true; do + case "$shell" in + ksh|sh|csh|tcsh) + break + ;; + + *) + error "Shell was not one of ksh, sh, csh or tcsh!" + prompt_for_field "Shell" + shell="$fieldvalue" + ;; + esac +done + +if [ "_$hostname" = "_" ]; then + verbose "Hostname was not specified!" + prompt_for_field "Hostname" + hostname="$fieldvalue" +fi + +if [ "$workstation" != "Win NT" ]; then + if [ "_$homeserver" = "_" ]; then + verbose "Home Server was not specified!" + prompt_for_field "Home Server" + homeserver="$fieldvalue" + fi +fi + +if [ "$workstation" = "Unix" ]; then + if [ "_$displayname" = "_" ]; then + displayname=$hostname + fi +elif [ "$workstation" = "X Terminal" ]; then + if [ "$displayname" = "$hostname" ]; then + verbose "Display name cannot be the same as hostname for an X Terminal" + prompt_for_field "Display name" + displayname="$fieldvalue" + fi + if [ "_$displayname" = "_" ]; then + prompt_for_field "Display name" + displayname="$fieldvalue" + fi +fi + +display_parms + +export PATH=$PATH:/app/admin/bin + +print "Add MOA Entry (Y/n)?\c" +read answer +answer=$(print "$answer" | tr [:upper:] [:lower:]) + +case $answer in + y|yes) + add_moa -username $username \ + -fullname "$fullname" \ + -phone $phone \ + -homeserver $homeserver \ + -shell $shell + ;; + *) + print "$fullname not added to MOA" + ;; +esac + +if [ "$employeetype" != "Contractor" ]; then + print "Add Synchronize Entry (Y/n)?\c" + read answer + answer=$(print "$answer" | tr [:upper:] [:lower:]) + + case $answer in + y|yes) + add_synchronize -username $username \ + -fullname "$fullname" \ + -groupname $groupname + ;; + *) + print "$fullname not added to Synchronize" + ;; + esac +fi + +print "Add Postnote Entry (Y/n)?\c" +read answer +answer=$(print "$answer" | tr [:upper:] [:lower:]) + +case $answer in + y|yes) + add_postnote -username $username \ + -fullname "$fullname" \ + -phone $phone \ + -hostname $hostname \ + -displayname $displayname + ;; + *) + print "$fullname not added to Postnote" + ;; +esac + +print "Add Shared/X Entry (Y/n)?\c" +read answer +answer=$(print "$answer" | tr [:upper:] [:lower:]) + +case $answer in + y|yes) + add_sharedx -username $username \ + -fullname "$fullname" \ + -phone $phone \ + -hostname $hostname \ + -displayname $displayname + ;; + *) + print "$fullname not added to Shared/X" + ;; +esac + +print "Send request for email account for $fullname (Y/n)?\c" +read answer +answer=$(print "$answer" | tr [:upper:] [:lower:]) + +case $answer in + y|yes) + if [ "$employeetype" != "Contractor" ]; then + add_email -username $username \ + -fullname "$fullname" \ + -employeetype $employeetype \ + -employeenumber $employeenumber \ + -manager "$manager" \ + -mailserver cllmail + else + add_email -username $username \ + -fullname "$fullname" \ + -employeetype $employeetype \ + -manager "$manager" \ + -mailserver cllmail + fi + ;; + *) + print "$fullname not added to email" + ;; +esac diff --git a/clients/HP/bin/adl-config b/clients/HP/bin/adl-config new file mode 100644 index 0000000..d879a8c --- /dev/null +++ b/clients/HP/bin/adl-config @@ -0,0 +1,437 @@ +#!/usr/bin/ksh +################################################################################ +# +# File: adl-config +# Description: ADL system configuration script +# To run this script you must have the adl-config.src parameter +# file located in root. It is important that the source parameter +# file be read and understood before running the script. +# See below for useful comments. +# Author: Kevin Lister - kel@cup.hp.com +# Date 3.11.99 +# Language: Korn Shell +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +# Revision History +# 3.25.99 kel Changed the name of the Clearcase install script in shell +# archive, so it had to be changed here as well. Added an +# eclipse install script to the shell archive, so a line to +# to remove it if Clearcase is not installed had to be added +# here as well. +# 4.1.99 kel Added code to determine if the installed system is going to have +# a graphics console. If yes, then the /etc/dt/config/Xservers +# file needs to have the console server line uncommented. +# Also added absolute paths to the unix commands. +# +################################################################################ +# Useful (hopefully) Comments Section +# +# This script will configure a system to operate nicely in the ADL +# infrastructure. This script requires the adl-config.src file in order to +# run. The adl-config.src file contains variables that determine exactly +# what type of optional software to install, which patch bundle to install, +# configures various system files, etc. +# +# Here is a brief description of what this script will do: +# +# 1) Check that the script is run as root +# 2) Check that the architecture is correct. The script will run on most +# hardware. The architecture is really only important when trying to +# determine which 100Mbit drivers to install. +# 3) Sources the input parameter source file. +# 4) Determine if script is run intereactive or not. +# 5) Determine if 100Mbit drivers are to be installed +# 6) If intereactive, greet the user and display the parameter settings. +# 7) Modify the kernel system file located in /stand/system +# 8) Download the shell archive file from the anonymous ftp server and unpack. +# The shell archive contains many files and symlinks and will not be +# listed here. See the README in the ahell archive build area and the shell +# archive itself for more details. One can also look through the "root" +# directory that is used to build the archive to see which files and +# symlinks are included. +# 9) Modify the /etc/rc.config.d files. (turn off unused stuff) +# 10) Modify the /etc/issue, /etc/gettydefs and /etc/motd files. +# 11) Modify miscellaneous files. +# 12) Perform miscellaneous setup procedures: +# a) Run /net/bismol/App/admin/bin/setup +# b) Run /usr/local/bin/ninstall -h bismol lp adm net3 +# c) Run /usr/adm/netdist/netdaemon.dy +# d) Run /usr/sbin/catman -w +# 13) Set the system up for ClearCase installation upon automatic reboot. +# 14) Install optional software and patches from the specified depot server. +# +# END of Useful Comments Section +################################################################################ + +# +## +### Variables +## +# + +SRC_FILE=/adl-config.src + +BASE=${0##*/} +HOST=`/bin/uname -n` +ARCH=`/bin/uname -m` +OS=`/bin/uname -r | /usr/bin/cut -c 3-4` +integer INDEX=0 + +# +## +### Functions +## +# + +function error { + print -u2 "\t$BASE: Error: $1" +} + +function warning { + print -u2 "\t$BASE: Warning: $1" +} + +function display { + print "\t$1" +} + +function usage { + display "\t$BASE [-usage]" + display " -usage: Print this usage message" + display " " + error "$1" + exit 1 +} + +function step { + let INDEX=INDEX+1 + display "\tStep #$INDEX: $@" +} + +function get_shar { +step "Get shell archive from ftp server and unpack" + cd / + ftp -n $FTP_SERVER <<@EOD +user $FTP_USER $FTP_PASSWD +cd $SHAR_DIR +get $SHAR_FILE +quit +@EOD +if [ $? -ne 0 ]; then + error "Unable to ftp $SHAR_FILE from $FTP_SERVER" + exit 1 +fi +sh $SHAR_FILE >> $LOGFILE 2>&1 +if [ $? -ne 0 ]; then + error "Cannot unpack shell archive." + exit 1 +fi +/bin/rm -f $SHAR_FILE +} + +function clearcase_setup { + if [ "$CLEARCASE" = "NO" ]; then + /bin/rm -f /sbin/rc3.d/S998install_clearcase + /bin/rm -f /sbin/rc3.d/S999install_eclipse + fi +} + +function chk_uid { + if [ $(id -u) -ne 0 ]; then + error "Must be root to execute this command... Exiting!" + exit 1 + fi +} + +function chk_arch { + case $ARCH in + 9000/7[1-3]*|9000/755|9000/7[7-8]*|9000/8**) + continue + ;; + + *) + warning "\tUnknown machine type $ARCH, Exiting!" + exit 1 + ;; + esac +} + +function read_src { + if [ -a $SRC_FILE ]; then + . $SRC_FILE + case "$CLEARCASE" in + y|Y|yes|YES|Yes|1) + CLEARCASE=yes + ;; + *) + CLEARCASE=no + ;; + esac + case "$SWINSTALL" in + y|Y|yes|YES|Yes|1) + SWINSTALL=yes + ;; + *) + SWINSTALL=no + ;; + esac + else + error "Source file does not exist!" + exit 1 + fi +} + +function set_mode { + case "$INTERACTIVE" in + y|Y|yes|YES|Yes|1) + INTERACTIVE=yes + ;; + *) + INTERACTIVE=no + ;; + esac +} + +function fast_enet { + if [ "_$ENET_DRVRS" = "_" ]; then + FAST_ENET=no + else + FAST_ENET=yes + fi +} + +function mod_kernel { + step "Modify /stand/system file." + grep -v -E 'maxswapchunks|default_disk_ir|nstrpty' /stand/system \ + > /stand/system.new + /bin/mv /stand/system /stand/system.orig + /bin/mv /stand/system.new /stand/system + + case $ARCH in + 9000/7[1-5]*) + echo "create_fastlinks 1" >> /stand/system + echo "dbc_max_pct 25" >> /stand/system + echo "default_disk_ir 1" >> /stand/system + echo "fs_async 1" >> /stand/system + echo "maxdsiz (256*1024*1024)" >> /stand/system + echo "maxfiles 256" >> /stand/system + echo "maxfiles_lim 2048" >> /stand/system + echo "maxssiz (80*1024*1024)" >> /stand/system + echo "maxswapchunks 4096" >> /stand/system + echo "maxuprc 500" >> /stand/system + echo "maxusers 150" >> /stand/system + echo "netmemmax 0" >> /stand/system + echo "nfile 7000" >> /stand/system + echo "nflocks 400" >> /stand/system + echo "ninode 20000" >> /stand/system + echo "nproc 1500" >> /stand/system + echo "npty 512" >> /stand/system + echo "nstrpty 512" >> /stand/system + echo "semmns 200" >> /stand/system + if [ "$OS" = "10" ]; then + echo "large_ncargs_enabled 1" >> /stand/system + fi + ;; + + 9000/7[7-8]*|9000/8**) + echo "create_fastlinks 1" >> /stand/system + echo "dbc_max_pct 25" >> /stand/system + echo "default_disk_ir 1" >> /stand/system + echo "fs_async 1" >> /stand/system + echo "maxdsiz (512*1024*1024)" >> /stand/system + echo "maxfiles 256" >> /stand/system + echo "maxfiles_lim 2048" >> /stand/system + echo "maxssiz (80*1024*1024)" >> /stand/system + echo "maxswapchunks 4096" >> /stand/system + echo "maxuprc 1000" >> /stand/system + echo "maxusers 256" >> /stand/system + echo "netmemmax 0" >> /stand/system + echo "nfile 14000" >> /stand/system + echo "nflocks 800" >> /stand/system + echo "ninode 40000" >> /stand/system + echo "nproc 3000" >> /stand/system + echo "npty 512" >> /stand/system + echo "nstrpty 512" >> /stand/system + echo "semmns 400" >> /stand/system + if [ "$OS" = "10" ]; then + echo "large_ncargs_enabled 1" >> /stand/system + fi + ;; + + *) + warning "Unknown machine model $ARCH!" + warning "Leaving kernel parameters as default" + /bin/mv /stand/system.orig /stand/system + ;; + esac +} # mod_kernel + +function greet { + display "\tADL System Configuration script." + display + display "\tYou are about to install and modify various system files," + display "\tinstall system patches, install optional software and," + display "\tif you elected to do so, install ClearCase 3.2." + display + display "\tIf you wish to modify the parameters below exit the install" + display "\tand modify the parameters in the $SRC_FILE file." + display + display "\tMachine Name:\t\t\t$MACHINE_NAME" + display "\tMachine Usage:\t\t\t$MACHINE_USAGE" + display "\tMacine Location:\t\t$LOCATION" + display "\tOwner's Fullname:\t\t$OWNER_NAME" + display "\tOwner's Email:\t\t\t$OWNER_EMAIL" + display "\tOwner's Extension:\t\t$OWNER_EXTENSION" + display "\tInstall ClearCase?:\t\t$CLEARCASE" + display "\tInstall 100Mbit Drivers?:\t$FAST_ENET" + if [ "$SWINSTALL" = "yes" ]; then + display "\tThe following products will be installed from $DEPOT:" + display "\t$PRODUCTS" + display + else + display + fi + if [ "$INTERACTIVE" = "yes" ]; then + display "\tContinue installation with these parameters (Y|n)?\c" + display + answer=y + read answer + case "$answer" in + y|Y|yes|Yes|YES|"") + continue + ;; + *) + display + display "\tYou have chosen NOT to run the $BASE setup script... +Exiting" + exit 1 + ;; + esac + fi +} # greet + +function mod_rc_files { + /usr/sbin/ch_rc -ap AUDIO_SERVER=0 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap LIST_TEMPS=0 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap CLEAR_TMP=1 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap HPARRAY_START_STOP=0 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap NIS_CLIENT=1 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap NIS_DOMAIN=adl >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap START_LLBD=0 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap NTPDATE_SERVER=cupertino.ntp.hp.com >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap XNTPD=1 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap NETTL=0 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap NUM_NFSIOD=16 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap VTDAEMON_START=0 >> $LOGFILE 2>&1 + if [ "$OS" = "10" ]; then + /usr/sbin/ch_rc -ap WAIT_FOR_NIS_SERVER=FALSE >> $LOGFILE 2>&1 + fi +} + +function mod_etc_files { + step "/etc files setup" + print "+auto.master" > /etc/auto_master + /bin/chmod 644 /etc/auto_master + /bin/chown root:root /etc/auto_master + + sed "s/GenericSysName/$MACHINE_NAME/" /etc/issue > /etc/issue-new + /bin/mv /etc/issue /etc/issue-orig + /bin/mv /etc/issue-new /etc/issue + + sed "s/Console Login:/$MACHINE_NAME Console Login:/" /etc/gettydefs \ + > /etc/gettydefs-new + /bin/mv /etc/gettydefs /etc/gettydefs-orig + /bin/mv /etc/gettydefs-new /etc/gettydefs + + /bin/banner $MACHINE_NAME > /etc/motd + /bin/uname -a >> /etc/motd + cat >> /etc/motd <<:END + +******************************************************************************* +* This is a private system operated for the Hewlett-Packard Company business. * +* Authorization from HP management is required to use this system. * +* Use by unauthorized persons is prohibited. * +******************************************************************************* +For System Support: Mon-Fri 8:00-5:00 Email (site-ux@cup.hp.com) +Phone: t-447-1212 After hours/weekend Pre-arrange: t-447-0629 +------------------------------------------------------------------------------- +Usage: $MACHINE_USAGE +Owner: $OWNER_NAME ($OWNER_EMAIL) Phone: $OWNER_EXTENSION +Location: $LOCATION +------------------------------------------------------------------------------- +:END + + sed "s/Root user/Root\@$HOST/" /etc/passwd > /tmp/passwd-new + /bin/mv /tmp/passwd-new /etc/passwd +} # mod_etc_files + +function mod_misc_files { + step "Miscellaneous file setup" + /bin/rm -f /var/adm/cron/at.allow + /bin/rm -f /var/adm/cron/cron.allow + /bin/chmod 644 /dev/lan* + case "$WORKSTATION" in + y|Y|yes|YES|Yes|1) + WORKSTATION=yes + ;; + *) + WORKSTATION=no + ;; + esac + if [ "$WORKSTATION" = "yes" ]; then + /bin/sed -e "s/# \*/ \*/" Xservers > /tmp/Xservers-new + /bin/mv /tmp/Xservers-new /etc/dt/config/Xservers + /bin/chmod 444 /etc/dt/config/Xservers + /bin/chown root:other /etc/dt/config/Xservers + fi +} + +function misc_setup { + step "Setup Application Server" + /net/bismol/App/admin/bin/setup >> $LOGFILE 2>&1 + + step "Ninstalling lp, adm and net3 packages" + /usr/local/bin/ninstall -h bismol lp adm net3 >> $LOGFILE 2>&1 + + step "Run netdaemon.dy" + /usr/adm/netdist/netdaemon.dy >> $LOGFILE 2>&1 + + step "Create the whatis database" + /usr/sbin/catman -w >> $LOGFILE 2>&1 +} + +function inst_sw { + if [ "$SWINSTALL" = "yes" ]; then + step "Installing Patches and Optional Software, be patient!" + /usr/sbin/swinstall -s $DEPOT -x $OPTIONS $PRODUCTS $ENETDRVR >> +$LOGFILE 2>&1 + else + step "Rebuilding kernel with new parameters." + /usr/sbin/mk_kernel -v -o /stand/vmunix >> $LOGFILE 2>&1 + step "Rebooting the system..." + cd / + /usr/sbin/shutdown -ry 0 + fi +} + +# +## +### Main +## +# + +chk_uid +chk_arch +read_src +set_mode +fast_enet +greet +mod_kernel +get_shar +mod_rc_files +mod_etc_files +mod_misc_files +misc_setup +clearcase_setup +inst_sw diff --git a/clients/HP/bin/adl-config.src b/clients/HP/bin/adl-config.src new file mode 100644 index 0000000..56086a6 --- /dev/null +++ b/clients/HP/bin/adl-config.src @@ -0,0 +1,163 @@ +################################################################################ +# +# File: adl-config.src +# Description: Parameter Source File for the ADL system configuration script +# adl-config. This file is required by the adl-config script +# in order to run. +# See below for useful comments. +# Author: Kevin Lister (C) - kel@cup.hp.com +# Date: 3.11.99 +# Language: Korn Shell +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +# Revision History +# 4.1.99 kel added the WORKSTATION variable. Setting the WORKSTATION +# variable to yes will set the machine up with a graphics console +# login using CDE. +# +################################################################################ +# Useful (hopefully) Comments Section +# +# Do not make your changes to the varibales in the comment section. Change +# the variables at the end of this file. +# +# Below you will find all of the ENV variables that the adl-config script +# will use to configure the system. +# Descriptions for these variables can be found below, read on. +# +# Set the INTERACTIVE ENV variable to yes if you wish to have a chance to +view +# the configuration parameters the script will use before proceeding. Set to +# no otherwise. +# +# INTERACTIVE=yes +# +# Set the WORKSTATION variable to yes if you are installing a system that +will +# have a graphics console monitor attached. Setting WORKSTATION to no +disbales +# CDE on the console. +# +# WORKSTATION=no +# +# You can have ClearCase 3.2 installed automatically by setting CLEARCASE +# to yes. If you do not want ClearCase, set to no. +# +# CLEARCASE=yes +# +# If you want the Patch bundle (see below) and Optional Software installed +# set SWINSTALL to yes. +# +# SWINSTALL=yes +# +# If you plan to use a 100Mbit network interface then set FAST_ENET to yes +# to have the correct drivers installed. Set to no if you don't. +# +# FAST_ENET=yes +# +# Set the location of the logfile for the configure script using the LOGFILE +# variable. +# +# LOGFILE=/adl-config.log +# +# The /etc/motd file will be set up with the information contained in the next +# several variables. The /etc/issue and /etc/gettydefs files will also be +# setup by using the MACHINE_NAME variable. You should set these to something +# that makes sense. +# +# OWNER_NAME="ADL Support" +# OWNER_EMAIL=adl-support@cup.hp.com +# OWNER_EXTENSION=t-447-5790 +# MACHINE_USAGE="X Terminal Server" +# LOCATION=RDC +# MACHINE_NAME=Generic +# +# The next several variables set up the depot server name, depot path and +# names of the software bundles and products to install. It is likely that you +# you will only need to change ENET_DRVRS. Set ENET_DRVRS to 100BT-HSC for a +# J282 (780). Set ENET_DRVRS to SX00306 for a 755. The default for XTERM_SVR +# is "". If you really don't want patches and optional software then set +# PATCHES and OPTIONAL to "". You should never need to modify PRODUCTS, DEPOT +# or OPTIONS. +# +# PATCHES=Patches-Generic +# OPTIONAL="OptionalSoftware SysMonSoftware VUEtoCDE" +# XTERM_SVR="ENWARE netstation" +# ENET_DRVRS=100BT-HSC +# ENET_DRVRS=SX00306 +# PRODUCTS="$PATCHES $OPTIONAL $XTERM_SVR $ENET_DRVRS" +# DEPOT=adliux01:/depots/10.20 +# OPTIONS="autoreboot=true" +# +# Finally, the next several variables set up the ftp server, directory and +# filename of the shell archive that the script uses to unpack all kinds +# of useful files and symlinks. You should never have to modify these. +# +# FTP_SERVER=15.0.98.138 +# FTP_USER=anonymous +# FTP_PASSWD=$LOGNAME@$(uname -n).cup.hp.com +# SHAR_DIR=productivity/adl-config +# SHAR_FILE=adl-config.shar +# +# END of Useful Comments Section +################################################################################ + +# +## +### Change these to suit your fancy. +## +# + +INTERACTIVE=yes +WORKSTATION=no +CLEARCASE=yes +SWINSTALL=yes +FAST_ENET=no +LOGFILE=/adl-config.log + +# +## +### /etc/motd setup +## +# + +OWNER_NAME="ADL Support" +OWNER_EMAIL=adl-support@cup.hp.com +OWNER_EXTENSION=t-447-5790 +MACHINE_USAGE="Change Me" +LOCATION=RDC +MACHINE_NAME=GENERIC + +# +## +### Patches and Optional Software +## +# + +PATCHES=Patches-Generic +OPTIONAL="OptionalSoftware SysMonSoftware VUEtoCDE" +XTERM_SVR="" +ENET_DRVRS="" +PRODUCTS="$PATCHES $OPTIONAL $XTERM_SVR $ENET_DRVRS" + +# +## +### Depot Server Information +## +# + +DEPOT=adliux01:/depots/10.20 +OPTIONS="autoreboot=true" + +# +## +### Shell Archive Server and Location +## +# + +FTP_SERVER=15.0.98.138 +FTP_USER=anonymous +FTP_PASSWD=$LOGNAME@$(uname -n).cup.hp.com +SHAR_DIR=productivity/adl-config +SHAR_FILE=adl-config.shar diff --git a/clients/HP/bin/allmach b/clients/HP/bin/allmach new file mode 100644 index 0000000..33d82c6 --- /dev/null +++ b/clients/HP/bin/allmach @@ -0,0 +1,111 @@ +#!/bin/ksh +################################################################################ +# +# File: allmach +# Description: Runs an arbitrary command on all machines +# Author: Andrew@DeFaria.com +# Created: Fri Apr 30 14:17:40 PDT 1999 +# Language: Korn Shell +# Modifications:Added trapping of INT so that you can abort a non-responding +# machine. +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +# Set machines +machines=${machines:-$adm_base/data/machines} + +if [ "$1" = "-f" ]; then + shift + machines="$1" + shift +fi + +if [ "$1" = "-r" ]; then + root_remsh=true + shift +fi + +if [ ! -f $machines ]; then + error "Unable to find $machines file!" 1 +fi + +function trap_intr { + display "${machines[i]}:$cmd interrupted" + display "(A)bort $me or (C)ontinue with next machine? \c" + read response + typeset -l response=$response + + case "$response" in + a|abort) + display "Aborting $me..." + exit + ;; + esac + display "Continuing on with the next machine..." +} # trap_intr + +# Build up data arrays. Note this is done because if we remsh while in a pipe +# Sun will not allow a simple remsh with no command (boo!) +# Column 1 Machine name +# Column 2 Model +# Column 3 OS Version +# Column 4 ClearCase Version (if applicable) +# Column 5 Owner (if known) +# Column 6 Usage (if known) +oldIFS=$IFS +IFS=":" +integer nbr_of_machines=0 +sed -e "/^#/d" $machines | + while read machine model osversion ccversion owner phone usage location; +do + machines[nbr_of_machines]=$machine + models[nbr_of_machines]=$model + #osversions[nbr_of_machines]=$osversion + #ccversions[nbr_of_machines]=$ccversion + #owners[nbr_of_machines]=$owner + #phones[nbr_of_machines]=$phone + #usages[nbr_of_machines]=$usage + #locations[nbr_of_machines]=$location + let nbr_of_machines=nbr_of_machines+1 +done +IFS="$oldIFS" + +# This loop executes the command +trap trap_intr INT +integer i=0 +while [ $i -lt $nbr_of_machines ]; do + export currmachine=${machines[i]} + # Execute command. Note if no command is given then the effect is to + # rlogin to each machine. + print -u2 "${machines[i]}\c" + print -u2 ":$@" + cmd="$@" + if [ $# -gt 0 ]; then + if [ "$root_remsh" = "true" ]; then + remsh ${machines[i]} -n -l root "$cmd" + else + remsh ${machines[i]} -n "$cmd" + fi + else + if [ "$root_remsh" = "true" ]; then + remsh ${machines[i]} -l root + else + remsh ${machines[i]} + fi + fi + let i=i+1 +done +trap - INT diff --git a/clients/HP/bin/arcserverenv b/clients/HP/bin/arcserverenv new file mode 100644 index 0000000..42f3abc --- /dev/null +++ b/clients/HP/bin/arcserverenv @@ -0,0 +1,66 @@ +#!/bin/ksh +################################################################################ +# +# File: arcservenv +# Description: Set environment variables for Arc Serve +# Author: Andrew@DeFaria.com +# Created: Fri Jul 2 14:49:22 PDT 1999 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +# Set machines +machines=${machines:-$adm_base/data/machines} + +if [[ "$me" = "-ksh" ]]; then + : #This script was sourced (I think, the man page is sketchy on this) +else + error "You need to invoke this script with a leading. (e.g. . $me)" 1 +fi + +me=$(basename $2) + +if [ "$VENDOR" = "Sun" ]; then + export ARC_HOME=${ARC_HOME:-/opt/ARCserve} + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/openwin/lib:$ARC_HOME:/usr/dt/lib +elif [ "$VENDOR" = "HP" ]; then + export ARC_HOME=${ARC_HOME:-/usr/ARCserve} + export SHLIB_PATH=$SHLIB_PATH:$ARC_HOME:/usr/dt/lib +else + export ARC_HOME=${ARC_HOME:-Unknown} +fi + +if [ ! -d "$ARC_HOME" ]; then + warning "$ARC_HOME does not exist!" +fi + +export PATH=$PATH:$ARC_HOME +export XFILESEARCHPATH=/usr/openwin/lib/locale/%L/%T/%N/%S:/usr/openwin/lib/%T/%N/%S +export HHHOME=$ARC_HOME +export MANPATH=$MANPATH:$ARC_HOME/man + +display "ARC Serve Environment Settings" +display - ------------------------------------------------------------------------------- +display "ARC_HOME: $ARC_HOME" +display "PATH: $PATH" +if [ "$VENDOR" = "Sun" ]; then + display "XFILESEARCHPATH: $XFILESEARCHPATH" + display "LD_LIBRARY_PATH: $LD_LIBRARY_PATH" +else + display "SHLIB_PATH: $SHLIB_PATH" +fi +display "HHHOME: $HHHOME" +display "MANPATH: $MANPATH" diff --git a/clients/HP/bin/atria.scs b/clients/HP/bin/atria.scs new file mode 100644 index 0000000..793b2b8 --- /dev/null +++ b/clients/HP/bin/atria.scs @@ -0,0 +1,727 @@ +#!/bin/sh +############################################# +# @(#)scs 1.26 +# system characterization script +# "scs" collects data from Atria +# customer sites for analysis by +# engineering. +###################################### + +######################### +# For Solaris 5.x systems +######################### +nddit() +{ +for i in tcp udp ip +do + /usr/sbin/ndd /dev/$i \? | awk '{print $1}' | \ + egrep -v '\?|directed|respond'> /tmp/$$ + for j in `cat /tmp/$$` + do + printf "%-30s %s\n" $j `/usr/sbin/ndd /dev/$i $j` + done + rm /tmp/$$ +done +} + +solaris() +{ +show "System Device Configuration" /usr/sbin/prtconf -vP +show "System Configuration" /usr/sbin/sysdef -i +show "System Customization" '/bin/cat /etc/system | \ + /bin/grep -v "^\*" | /bin/grep -v "^$"' +show "Network Configuration" nddit +show "System Messages" /bin/dmesg +show "Patches" /bin/showrev -p + +show "Processes" '/usr/ucb/ps auxww | /bin/fold -80' +show "Network Configuration" /usr/sbin/ifconfig -a +show "Network Utilization Summary" /bin/netstat -i +show "Network Protocol Statistics" /bin/netstat -s +show "Network Mbuf Statistics" /bin/netstat -m +show "VM Statistics" /bin/vmstat -s +show "Cache Flush Statistics" /bin/vmstat -c +show "Interrupts" /bin/vmstat -i +show "NFS Statistics" /bin/nfsstat +show "NFS Responses" /bin/nfsstat -m + +show "CPU Utilization" /bin/sar -u 1 10 +show "Buffer Activity" /bin/sar -b 1 10 +show "Block Device Activity" /bin/sar -d 1 10 +show "Disk Device Activity" iostat -x 1 10 +show "Paging In Activity" /bin/sar -p 1 5 +show "Paging Out Activity" /bin/sar -g 1 5 +show "Free Memory" /bin/sar -r 1 5 +show "vmstat snapshot" /bin/vmstat 2 10 +show "Kernel Memory Allocation Activity" /bin/sar -k 1 5 +show "KMA statistics" echo kmastat | /usr/sbin/crash +echo "" +} + +####################### +# For SUNOS 4.x systems +####################### +sunos() +{ + +show "Memory " '/etc/dmesg | grep mem' +show "System Messages" /etc/dmesg +show "Processes" '/bin/ps auxww | /usr/ucb/fold -80' +show "Network Utilization Summary" /usr/ucb/netstat -i +show "Network Protocol Statistics" /usr/ucb/netstat -s +show "Network Mbuf Statistics" /usr/ucb/netstat -m +show "VM Statistics" /usr/ucb/vmstat -s +show "Cache Flush Statistics" /usr/ucb/vmstat -c +show "Interrupts" /usr/ucb/vmstat -i +show "NFS Statistics" /usr/etc/nfsstat + +show "Disk Device Activity" /bin/iostat -D 1 10 +show "CPU Activity" /usr/ucb/vmstat 1 10 +show "Tables" /etc/pstat -T +show "vmstat snapshot" /usr/ucb/vmstat 2 10 + +} + + +####################### +# For AIX +####################### +aix() +{ + +show "Processes" '/usr/bin/ps -elf ' +show "Network Utilization Summary" /bin/netstat -i +show "Network Protocol Statistics" /bin/netstat -s +show "Network Mbuf Statistics" /bin/netstat -m +show "Network Configurables" /usr/sbin/no -a +show "NFS Statistics" /usr/sbin/nfsstat + +show "System Attributes" /usr/sbin/lsattr -E -l sys0 +show "CPU Utilization" sar -u 1 10 +show "Buffer Activity" sar -b 1 10 +show "Disk Activity" /bin/iostat 1 10 +show "Paging Stats" sar -r 1 5 +show "Network Monitor for 30s" '/bin/netpmon -v -o /tmp/$$; sleep 30; trcstop' +echo "" +} + +####################### +# For OSF1 +####################### +osf1() +{ +show "Boot Messages" 'echo "";echo ""; echo "";uerf -r 300 | tail -100' +show "Processes" '/usr/bin/ps glww | fold -80' +show "Network Utilization Summary" /usr/sbin/netstat -i +show "Network Protocol Statistics" /usr/sbin/netstat -s +show "Network Mbuf Statistics" /usr/sbin/netstat -m +show "NFS Statistics" /usr/bin/nfsstat +show "Kernel memory usage" /usr/bin/vmstat -M +show "vmstat snapshopt" /usr/bin/vmstat 2 10 +show "Disk Device snapshot" /bin/iostat 2 10 + +} + +####################### +# For IRIX 5.x +####################### +irix() +{ +show "System Device Configuration" /usr/sbin/sysconf +show "System Hardware Configuration" /usr/bin/hinv +show "System Software Configuration" /etc/chkconfig +show "Disk Usage" /usr/sbin/df -l -k +show "System Customization" '/bin/cat /var/sysgen/stune | \ + /bin/grep -v "^\*" | /bin/grep -v "^$"' + +show "Processes" '/usr/bin/ps -elf | /bin/fold -80' +show "Network Utilization Summary" /usr/etc/netstat -i +show "Network Protocol Statistics" /usr/etc/netstat -s +show "Network Mbuf Statistics" /usr/etc/netstat -m +show "NFS Statistics" /usr/etc/nfsstat + +show "CPU Utilization" sar -u 1 10 +show "Buffer Activity" sar -b 1 10 +show "Block Device Activity" sar -d 1 10 +show "Paging In Activity" sar -p 1 5 +show "Paging Out Activity" sar -g 1 5 +show "Free Memory" sar -r 1 5 +echo "" +} + + +##################### +# For HP-UX 9/800 systems +##################### +hpux98() +{ +#first the shared info +hpux +# now the system specific +show "System definition" /etc/sysdef +show "CPU Utilization" /usr/bin/sar -u 1 10 +show "Buffer Activity" /usr/bin/sar -b 1 10 +show "Block Device Activity" /usr/bin/sar -d 1 10 +} +##################### +# For HP-UX 9/700 systems +##################### +hpux97() +{ +# just the shared info +hpux +} +##################### +# For All HP-UX 9 systems +##################### +hpux() +{ +show "Memory " grep Physical /usr/adm/messages +show "System Messages" /etc/dmesg +show "Swap Space" /etc/swapinfo +show "Patches" ls -ld /system/PH* +show "IO devices" /etc/ioscan -f + +show "Processes" '/bin/ps -elf | /usr/bin/fold -80' +show "Network Utilization Summary" /usr/bin/netstat -i +show "Network Protocol Statistics" /usr/bin/netstat -s +show "Network Mbuf Statistics" /usr/bin/netstat -m +show "VM Statistics" /usr/bin/vmstat -s +show "NFS Statistics" /usr/etc/nfsstat +show "Network errors" /etc/netfmt -t 30 -f /usr/adm/nettl.LOG00 + +show "Disk Utilization" df -l +show "vmstat snapshot" /usr/bin/vmstat 2 10 +} + +##################### +# For HP-UX 10 systems +##################### +hpux10() +{ + +show "Memory " grep Physical /var/adm/syslog/syslog.log +show "System DMessages" /usr/sbin/dmesg +show "System definition" /usr/sbin/sysdef +show "Messages" "tail -100 /usr/adm/syslog/syslog.log" +show "IO status" /etc/ioscan -f +show "Patches" '/usr/sbin/swlist -l product | grep PH' + +show "Processes" '/usr/bin/ps -elf | /usr/bin/fold -80' +show "Network Utilization Summary" /usr/bin/netstat -i +show "Network Protocol Statistics" /usr/bin/netstat -s +show "Network Mbuf Statistics" /usr/bin/netstat -m +show "VM Statistics" /usr/bin/vmstat -s +show "NFS Statistics" /usr/bin/nfsstat + +show "CPU Utilization" /usr/bin/sar -u 1 10 +show "Buffer Activity" /usr/bin/sar -b 1 10 +show "Block Device Activity" /usr/bin/sar -d 1 10 +show "vmstat snapshot" /usr/bin/vmstat 2 10 +} +##################### +# For Unixware systems +##################### +unixware() +{ +show "Memory size" /sbin/memsize +show "Message log" cat /usr/adm/log/osmlog +show "System Configuration" /usr/sbin/sysdef +show "Processes" '/usr/bin/ps -elf ' +show "Network Utilization Summary" /bin/netstat -i +show "Network Protocol Statistics" /bin/netstat -s +show "NFS Statistics" /usr/sbin/nfsstat + +show "CPU Utilization" /sbin/sar -u 1 10 +show "Buffer Activity" /sbin/sar -b 1 10 +show "Block Device Activity" /sbin/sar -d 1 10 +show "Paging In Activity" /sbin/sar -p 1 5 +show "Paging Out Activity" /sbin/sar -g 1 5 +show "Free Memory" /sbin/sar -r 1 5 +show "Historical sar data" /sbin/sar -A +echo "" +} + + +#################################### +# display informationin a uniform way +#################################### +show() { + echo "----------------------------------------------------" + echo $1 + echo "----------------------------------------------------" + shift + eval "$@" + echo "" +} +################################################## +# check uw network - works for Unixware +################################################## +check_uw_network() { + +netstat -i $interface +sleep 10 +netstat -i $interface +} + +################################################## +# check network - works for HP-UX and SUNOS 5.x +################################################## +check_network() { + printf "%8s %8s %8s %8s %8s %8s \n" time inpkts inerrs \ + outpkts outerrs colls + netstat -i -I $interface $interval | ( + (line ; line ; line) > /dev/null + t_inpkts=0 # initialize counter + t_inerrs=0 # initialize counter + t_outpkts=0 # initialize counter + t_outerrs=0 # initialize counter + t_colls=0 # initialize counter + i=0 + while test $i -lt $count ; do # for each of the lines + time=`date +%T` + /bin/echo $time \\c + set -- `line` # get the line + printf "%8s %8s %8s %8s %8s\n" $1 $2 $3 $4 $5 + t_inpkts=`expr $1 + $t_inpkts` # accumulate in packets + shift + t_inerrs=`expr $1 + $t_inerrs` # accumulate in errors + shift + t_outpkts=`expr $1 + $t_outpkts` # accumulate out packets + shift + t_outerrs=`expr $1 + $t_outerrs` # accumulate out errors + shift + t_colls=`expr $1 + $t_colls` # accumulate collisions + i=`expr $i + 1 ` + done + printf "\n%8s %8s %8s %8s %8s %8s \n" \ + total $t_inpkts $t_inerrs $t_outpkts $t_outerrs $t_colls +# now check error and collision rate. +# Use awk to get floating point accuracy + echo $t_colls $t_outpkts $t_inerrs $t_inpkts | awk '$2 != 0 { + collision_rate = $1 / $2; + printf("\n\ncollision rate ( %g %% )", (collision_rate * 100.0)); + if ( collision_rate > 0.05 ) + printf(" too high. Add subnets.\n"); + else + printf("\n")} + $4 != 0 { + error_rate = $3 / $4; + printf(" error rate ( %g %% )", (error_rate * 100.0)); + if (error_rate > 0.00025) + printf(" too high. Check cabling.\n"); + else + printf("\n")}' + + ) +} + +################################################## +# check network - works for SUNOS 4.x +################################################## +check_network1() { + echo " time inpkts inerrs outpkts outerrs colls" + netstat -i -I $interface $interval | ( + (line ; line ; line) > /dev/null + t_inpkts=0 # initialize counter + t_inerrs=0 # initialize counter + t_outpkts=0 # initialize counter + t_outerrs=0 # initialize counter + t_colls=0 # initialize counter + i=0 + while test $i -lt $count ; do # for each of the lines + time=`date +%T` + /bin/echo -n $time + set -- `line` # get the line + echo " $1 $2 $3 $4 $5" + t_inpkts=`expr $1 + $t_inpkts` # accumulate in packets + shift + t_inerrs=`expr $1 + $t_inerrs` # accumulate in errors + shift + t_outpkts=`expr $1 + $t_outpkts` # accumulate out packets + shift + t_outerrs=`expr $1 + $t_outerrs` # accumulate out errors + shift + t_colls=`expr $1 + $t_colls` # accumulate collisions + i=`expr $i + 1 ` + done + echo -n "total " + echo -n "$t_inpkts $t_inerrs $t_outpkts " + echo " $t_outerrs $t_colls" +# now check error and collision rate. +# Use awk to get floating point accuracy + echo $t_colls $t_outpkts $t_inerrs $t_inpkts | awk '$2 != 0 { + collision_rate = $1 / $2; + printf("\n\ncollision rate ( %g %% )", (collision_rate * 100.0)); + if ( collision_rate > 0.05 ) + printf(" too high. Add subnets.\n"); + else + printf("\n")} + $4 != 0 { + error_rate = $3 / $4; + printf(" error rate ( %g %% )", (error_rate * 100.0)); + if (error_rate > 0.00025) + printf(" too high. Check cabling.\n"); + else + printf("\n")}' + + ) +} + +######################################################## +# establish file partition -> disk device mapping for solaris +# how do I do this for other platforms? +######################################################## +do_discs() +{ +df -F ufs +df -F ufs | cut -f1 -d: | awk '{print $2}' | sed 's/(//' |\ + sed 's/)//' > /tmp/lll +for i in `cat /tmp/lll` +do + ls -l $i +done +rm /tmp/lll +cat /etc/path_to_inst +} + +######################################################## +# calculate the size (in bytes) of all VOB database on this host +######################################################## +check_vobs() +{ +if [ ! -f $ATRIAHOME/bin/cleartool ] ; then + echo this is not a ClearCase host + exit +fi +# list all vobs on this host and extract the +# VOB storage directory. +$ATRIAHOME/bin/cleartool lsvob -host $HOST +$ATRIAHOME/bin/cleartool lsvob -host $HOST | \ +awk '$1 == "*" {print $3 } \ + $1 != "*" {print $2 }' >/tmp/list.$$ +# count the vobs +vobs=`wc -l /tmp/list.$$ | awk '{print $1}'` +echo "Number of vobs : $vobs" +if [ $vobs = 0 ] ; then + return +fi +# now count all the bytes in the database data and key files +for i in `cat /tmp/list.$$` +do + cd $i/db + $LSL vob_db.d0? vob_db.k0? | \ + awk 'BEGIN {sum=0} \ + {sum = sum + $5} \ + END {printf "%12d\t", sum}' + echo `basename $i` +done | tee -a /tmp/list1.$$ + +awk 'BEGIN {sum=0} \ +{sum = sum + $1} \ +END {printf "%12d\tTOTAL", sum}' < /tmp/list1.$$ + +rm /tmp/list.$$ /tmp/list1.$$ +} + +######################################################## +# check the view characteristics +######################################################## +check_views() +{ +if [ ! -f $ATRIAHOME/bin/cleartool ] ; then + echo this is not a ClearCase host + exit +fi +$ATRIAHOME/bin/cleartool lsview -host $HOST | \ +awk '$1 == "*" {print $3,$2 } \ + $1 != "*" {print $2,$1 }' >/tmp/list.$$ +# count the views +views=`wc -l /tmp/list.$$ | awk '{print $1}'` +echo "Number of views : $views" +if [ $views = 0 ] ; then + return +fi +cat /tmp/list.$$ | ( +i=1 +while [ $i -le $views ] +do + set -- `line`; + size=`grep -s cache $1/.view | awk '{print $2}'` + if [ a$size = a ]; then + size=default + fi + echo "$size $2" | awk '{printf "%8s\t%-16s\n", $1, $2}' + i=`expr $i + 1` +done +) +rm /tmp/list.$$ +} + +######################################################## +# check the MVFS cache on this system +######################################################## +check_mvfs() +{ +/usr/atria/etc/mvfsstat -iclrh 2>&1 +} + +######################################################## +# check clearcase things +######################################################## +check_cc() +{ +ATRIAHOME=${ATRIAHOME:-/usr/atria} + show "VOB sizes" check_vobs + show "Views" check_views + show "MVFS" check_mvfs + show "Cleartool Version" $ATRIAHOME/bin/cleartool -ver +} + +######################################################## +# obtain mvs parameters +######################################################## + +get_mvfs_sun4() +{ +adb -k /var/adm/atria/vmunix_mvfs /dev/mem </dev/null < by Andrew@DeFaria.com +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) + +function usage { + print "usage: $me: [ -verbose | -v ] [ -size | -s n ] [ ]" + print "\t\t[ -top n | -t n ] [ -notop | -not ]\n" + print "Where:" + print " -size | -s n\tShow only files bigger then n Meg (default 1 Meg)" + print " -verbose | -v\tTurn on verbose mode (default verbose off)" + print " -top | -t n\tPrint out only the top n largest files (default LINES - 1)" + print " -notop|not\tPrint out all files (default top LINES - 1)" + print " \tFilesystems to check (default all hfs filesystems)" + exit +} # usage + +filesystems= +verbose=off +integer top_n +lines=${LINES:-25} +let top_n=$lines-1 +bytes_in_meg=1048576 +block_size=512 + +# Now get parms +integer size_in_meg=1 +integer size=$size_in_meg*$bytes_in_meg/$block_size + +while [ $# -gt 0 ]; do + case "$1" in + -size|-s) + shift + size_in_meg=$1 + # Convert size to 512 blocks + size=$size_in_meg*$bytes_in_meg/$block_size + shift + ;; + + -usage) + usage + ;; + + -top|-t) + shift + top_n=$1 + shift + ;; + + -notop|-not) + top_n=0 + shift + ;; + + -verbose|-v) + verbose=on + shift + ;; + + -*) + print -u2 "$me: Error: Unknown option $1" + print + usage + ;; + + *) + filesystems="$filesystems $1" + shift + ;; + esac +done + +if [ "_$filesystems" = "_" ]; then + i=1 + df -k -F ufs | while read line; do + if [ $i -gt 1 ]; then + filesystems="$filesystems $(echo $line | awk '{print $6}')" + fi + let i=$i+1 + done +fi + +# Now do the find +if [ $verbose = "on" ]; then + print "Filesystems:\t$filesystems" + print "Size:\t\t$size_in_meg Meg ($size blocks)" + print "Top:\t\t$top_n" +fi + +if [ $top_n -eq 0 ]; then + head_cmd="cat" +else + head_cmd="head -$top_n" +fi + +find $filesystems -xdev -size +$size -exec ls -l {} \; |\ + sort +nr5 | awk '{ printf ("%.3f %s\t%s\n", $5/(1024*1024), $3, $9) }' | + $head_cmd diff --git a/clients/HP/bin/buildservers b/clients/HP/bin/buildservers new file mode 100644 index 0000000..0eb3072 --- /dev/null +++ b/clients/HP/bin/buildservers @@ -0,0 +1,39 @@ +#!/bin/ksh +################################################################################ +# +# File: buildservers +# RCS: $Header: buildservers,v 1.3 98/01/28 12:21:43 defaria Exp $ +# Description: A script to execute a command on all build servers. +# Author: Andrew DeFaria, California Language Labs +# Created: Wed Mar 5 16:31:13 PST 1997 +# Modified: Fri Jan 16 13:51:43 PST 1998 +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +PATH=/adm/bin:$PATH + +if [ "$1" = "-r" ]; then + root=yes + shift +fi + +for buildserver in $(get_info server_names buildserver); do + # Execute command. Note if no command is given then the effect is to + # rlogin to each machine. + print "$buildserver:$@" + if [ $# -gt 0 ]; then + if [ -z "$root" ]; then + remsh $buildserver -n "$@" + else + root remsh $buildserver -n "$@" + fi + else + if [ -z "$root" ]; then + remsh $buildserver + else + root remsh $buildserver + fi + fi +done diff --git a/clients/HP/bin/check_security b/clients/HP/bin/check_security new file mode 100644 index 0000000..b20008c --- /dev/null +++ b/clients/HP/bin/check_security @@ -0,0 +1,93 @@ +#! /bin/ksh +USAGE='USAGE: check_security + + This script checks for some security problems. It does + not fix anything. It only prints messages about possible + problems. + + Author: Michael Coulter +' + +# Set parameters + + PASSWD_FILE=/etc/passwd + +# Check for execution by root + + WHOAMI=$(whoami) + if [ "$WHOAMI" != "root" ] + then + echo "It is recommended that you run this script as root" + fi + +# Parse all the lines in $PASSWD_FILE + + OLD_IFS="$IFS" + IFS=":" + cat "$PASSWD_FILE" | while read USER PASSWORD UID GID COMMENT HOME SHELL REST + do + # Checks for users who shouldn't log-in, i.e. PASSWORD is "*" + + if [ "$PASSWORD" = '*' ] + then + # If the PASSWORD is "*", there should not be a .rhosts or hosts.equiv + # in the home directory or .forward + if [ -f "${HOME}/.rhosts" ] + then + echo "$USER has a .rhosts file in $HOME" + fi + if [ -f "${HOME}/.forward" ] + then + echo "$USER has a .forward file in $HOME" + fi + + + + # There should not be a crontab or atjob for the user + + if [ -f "/usr/spool/cron/crontabs/${USER}" ] + then + echo "$USER has a crontab file in /usr/spool/cron/crontabs" + fi + if [ -f "/usr/spool/cron/atjobs/${USER}" ] + then + echo "$USER has a crontab file in /usr/spool/cron/atjobs" + fi + + fi # End of * password checks + + if [ "$PASSWORD" = "" ] + then + echo "$USER has a NULL password." + fi + + # No wildcards in $HOME/.rhosts or /etc/host.equiv + LINES="$(sed -e "/^#/d" $HOME/.rhosts | grep "+" 2> /dev/null | wc -l)" + if [ "$LINES" -ne 0 ] + then + echo "$USER has + in $HOME/.rhosts" + fi + + done + # read USER PASSWORD UID GID COMMENT HOME SHELL REST + +# Checks that are only done once + +# Check no wildcards in /etc/host.equiv + + LINES="$(grep -- "+" /etc/host.equiv 2> /dev/null | wc -l)" + if [ "$LINES" -ne 0 ] + then + echo "System has + in /etc/host.equiv" + fi + + if [ ! -f "/usr/adm/inetd.sec" ] + then + echo "No /usr/adm/inetd.sec file. " + fi + + if [ -f "/etc/hosts.equiv" ] + then + echo "System has a /etc/hosts.equiv file" + fi + diff --git a/clients/HP/bin/cleantmps b/clients/HP/bin/cleantmps new file mode 100644 index 0000000..fc9a35f --- /dev/null +++ b/clients/HP/bin/cleantmps @@ -0,0 +1,59 @@ +#!/bin/ksh +# +# $Header: cleantmps,v 1.14 97/07/24 12:17:34 root Exp $ +# +# +# This is a shell script to clean up files that clutter the tmp directories +# and garbage files that take up a lot of space (e.g., core files). +# +# +# Clean up specific garbage files, old log files +# +find /tmp -mountstop -mtime +1 -name "sh[1-9]*.[1-9]*" -exec rm {} \; +find /tmp -mountstop -mtime +3 -name "*~" -exec rm {} \; +find /tmp -mountstop -mtime +3 -name "eXT*" -exec rm {} \; +find /tmp -mountstop -mtime +3 -name "clr*" -exec rm {} \; +find /var/tmp -mountstop -mtime +3 -name "*~" -exec rm {} \; +find /tmp -mountstop -mtime +7 -name "crout*" -exec rm {} \; +find /var/tmp -mountstop -mtime +7 -name "cscope[0-9]*" -exec rm {} \; + +# +# Delete crash core dumps and kernel backups (leave behind INDEX (log) file) +# +find /var/adm/crash -type f -name "core.*" -exec rm {} \; +find /var/adm/crash -type f -name "vmunix*" -exec rm {} \; + +# +# General aging of everything in /tmp +# + +# Preserve sockets and certain files which I don't want to age away +find /tmp -mountstop -depth -mtime +7 ! -type d ! -type s ! -type p | + egrep -v "\.log$|\.X11-unix" | xargs -n25 rm + +# Try to remove directories -- only empty ones will actually get removed. +find /tmp -mountstop -depth -type d ! -name lost+found -print | + xargs -i -n25 rmdir -f {} 2>/dev/null + +# +# Search entire filesystem for files which are automatically cleaned up. +# +# Remove #* and core files. Exclude *.flc (emacs font-lock (fast-lock) +# files, which can start with '#'). +# +find / \( -fsonly hfs -o -fsonly vxfs \) -mtime +3 -type f \ +\( -name "#*" -o -name "core" \) ! -name "*.flc" | xargs rm -f + +# PJJ NOTE ON THE ABOVE COMMAND: +# +# By including only hfs and vxfs filesystems, we avoid descending NFS or +MVFS +# (ClearCase) mount points. We don't use "! -fstype nfs" -- that would +still +# descend NFS mounts, looking for HFS/VxFS mounted underneath. One could +# argue that it's preferable to do something like +# +# find / \( -fstype nfs -o -fstype mvfs \) -prune -o -mtime +3 ... +# +# (i.e., only list filesystem types to exclude), but the mention of mvfs +# would cause the command to fail on hosts which don't have ClearCase. diff --git a/clients/HP/bin/config_disk_array b/clients/HP/bin/config_disk_array new file mode 100644 index 0000000..528aff9 --- /dev/null +++ b/clients/HP/bin/config_disk_array @@ -0,0 +1,189 @@ +#!/usr/bin/ksh +################################################################################ +# +# File: config_disk_array +# RCS: $Header: config_disk_array,v 1.2 97/04/21 13:27:19 defaria Exp $ +# Description: A script to configure a NIKE Model 20 Disk Array +# Author: Andrew DeFaria, California Language Labs +# Created: Tue Jan 28 15:59:11 PST 1997 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) + +if [ $(id -u) -ne 0 ]; then + print -u2 "$me: Error: Must be root to execute this command!" + exit 1 +fi + +# Get parametes +primary_disk= +mirror_disk= +while [ $# -ge 1 ]; do + case "$1" in + -p) + if [ $# -le 1 ]; then + print -u2 "$me: Error: Primary disk not specified" + fi + shift + primary_disk="$1" + ;; + + -m) + if [ $# -le 1 ]; then + print -u2 "$me: Error: Mirror disk not specified" + fi + shift + mirror_disk="$1" + ;; + + -d|-debug) + debug=yes + ;; + + *) + print -u2 "$me: Error: Unknown parameter found ($1)" + exit 1 + ;; + esac + shift +done + +print "This script will configure the NIKE Model 20 Disk Array" + +if [ "_$primary_disk" != "_" ]; then + print "The primary disk is at: /dev/dsk/$primary_disk" +fi + +if [ "_$mirror_disk" != "_" ]; then + print "The mirror disk is at: /dev/dsk/$mirror_disk" +fi + +print + +if [ "_$primary_disk" = "_" -a "_$mirror_disk" = "_" ]; then + print -u2 "Nothing to do!" + exit 1 +fi + +answer=y +print "Are these settings correct (Y/n)?\c" +read answer + +if [ "$answer" != "y" -a "$answer" != "Y" ]; then + print -u2 "Nothing done" + exit 1 +fi + +# First create the mirror disk +if [ "_$mirror_disk" != "_" ]; then + print "Creating the mirror disk" + /sbin/pvcreate -f /dev/rdsk/$mirror_disk + status=$? + + if [ $status -eq 0 ]; then + print "Mirror disk created" + else + print "Unable to create mirror disk (Status: $status)" + exit 1 + fi +fi + +# Create Physical Volume Groups +if [ "_$primary_disk" != "_" ]; then + print "Creating Physical Volume Groups" + /sbin/vgextend -g primary /dev/vgvobs /dev/dsk/$primary_disk + status=$? + + # Ignore the warning about the volume already being created (Status: 2) + if [ $status -eq 0 -o $status -eq 2 ]; then + print "Physical Volume Group \"primary\" created" + else + print "Unable to create Physical Volume Group \"primary\" (Status: $status)" + exit 1 + fi +fi + +if [ "_$mirror_disk" != "_" ]; then + /sbin/vgextend -g mirror /dev/vgvobs /dev/dsk/$mirror_disk + status=$? + + if [ $status -eq 0 ]; then + print "Physical Volume Group \"mirror\" created" + else + print "Unable to create Physical Volume Group \"mirror\" (Status: $status)" + exit 1 + fi +fi + +if [ "_$primary_disk" = "_" ]; then + exit +fi + +# Create CLO logical volume +print "Creating CLO Logical Volume" + +if [ "_$mirror_disk" = "_" ]; then + /sbin/lvcreate -l 3004 -n CLO -r y -C n -s y -p w -d p vgvobs +else + /sbin/lvcreate -l 3004 -n CLO -m 1 -r y -C n -M y -s g -p w -d p vgvobs +fi + +status=$? + +if [ $status -eq 0 ]; then + print "CLO Logical Volume created" +else + print "Unable to create CLO Logical Volume (Status: $status)" + exit 1 +fi + +# Create the file system +print "Creating file system on CLO Logical Volume" +/usr/sbin/newfs -F hfs -L -i 6144 -m 5 /dev/vgvobs/rCLO +status=$? + +if [ $status -eq 0 ]; then + print "File system for CLO Logical Volume created" +else + print "Unable to create file system for CLO Logical Volume (Status: $status)" + exit 1 +fi + +# Mount the new CLO logical volume +print "Mounting CLO Logical Volume" +mkdir -p /CLO +/usr/sbin/mount -o rw,suid, -F hfs /dev/vgvobs/CLO /CLO +status=$? + +if [ $status -eq 0 ]; then + print "CLO Logical Volume mounted" +else + print "Unable to mount CLO Logical Volume (Status: $status)" + exit 1 +fi + +# Add the /etc/fstab entry +print "/dev/vgvobs/CLO /CLO hfs rw,suid 0 2" >> /etc/fstab +print "Added CLO Logical Volume to /etc/fstab" + +# Add the /etc/exports entry +print "/CLO -async" >> /etc/exports +print "Added CLO Logical Volume to /etc/exports as -async" + +# Export /CLO +print "Exporting CLO Logical Volume" +/usr/sbin/exportfs -a +status=$? + +if [ $status -eq 0 ]; then + print "CLO Logical Volume exported" +else + print "Unable to export CLO Logical Volume (Status: $status)" + exit 1 +fi + +print "Done" diff --git a/clients/HP/bin/configure_machine b/clients/HP/bin/configure_machine new file mode 100644 index 0000000..7adc6aa --- /dev/null +++ b/clients/HP/bin/configure_machine @@ -0,0 +1,325 @@ +#!/bin/ksh +################################################################################ +# +# File: configure_machine +# Description: A script to set up the "admin" environment +# Author: Andrew@DeFaria.com +# Created: Tue Apr 15 14:20:02 PDT 1997 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +if [ ! -d /adm ]; then + if [ ! -d "$adm_base" ]; then + print -u2 "$me: Error: Unable to find \"adm\" path! - Exiting" + exit 1 + fi +fi + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +# Source in tmpfiles function +tmpprefix=${TMPDIR:-/tmp}/configure_machine.$$ +. $adm_fpath/tmpfiles +arm_trap + +verbose= +debug= +mode=check + +function usage { + info "$ME [-v|verbose] [-d|debug] [-usage]" + info " -v|verbose: Turns on verbose mode" + info " -d|debug: Turns on debug mode" + info " -f|ix: Turns on fix mode" + info " -c|heck: Turns on check mode (default)" + info " -usage: Print this usage message\n" + + error "$1" 1 +} # usage + +function symlink { + debug "ENTER symlink" + from="$1" + to="$2" + + if [ ! -h "$from" ]; then + if [ $mode = "fix" ]; then + verbose "Setting up symlink from $from -> $to" + ln -s "$to" "$from" + else + warning "$from link is not setup properly" 0 + fi + else + verbose "$from link is OK" + fi + + debug "EXIT symlink" +} # symlink + +function setup_symlinks { + debug "ENTER setup_symlinks" + + symlinks="\ +/adm $adm_base\ +" + print $symlinks | while read from to; do + symlink $from $to + done + + debug "EXIT setup_symlinks" +} # setup_symlinks + +function check_and_replace_file { + debug "ENTER check_and_replace_file" + master_file="$1" + check_file="$2" + permissions="$3" + owner_group="$4" + + if ! cmp -s $master_file $check_file; then + if [ $mode = "fix" ]; then + verbose "Fixing $check_file" + cp $master_file $check_file + chmod $permissions $check_file + chown $owner_group $check_file + else + warning "$check_file is not setup properly" 0 + fi + else + verbose "$check_file is OK" + fi + + debug "EXIT check_and_replace_file" +} # check_and_replace_file + +function check_nsswitch_conf { + debug "ENTER check_nsswitch_conf" + + sed 's/automount: files nis/automount: nis files/' \ + /etc/nsswitch.conf > $tmpprefix.nsswitch.conf + + check_and_replace_file $tmpprefix.nsswitch.conf /etc/nsswitch.conf 444 +root:adm + + debug "EXIT check_nsswitch_conf" +} # check_nsswitch_conf + +function check_automount_maps { + debug "ENTER check_automount_maps" + + for map in master home direct indirect; do + check_and_replace_file /adm/etc/auto_$map /etc/auto_$map 444 root:adm + done + + debug "EXIT check_automount_maps" +} # check_automount_maps + +function setup_rcfiles { + debug "ENTER setup_rcfiles" + local_src=$ADM_PATH/etc/init.d/local + local_dest=/etc/init.d/local + local_symlink=/etc/rc3.d/S99local + + if [ ! -x $local_dest ]; then + if [ $mode = "fix" ]; then + verbose "Creating $local_dest" + cp "$local_src" $local_dest + chown root:adm $local_dest + chmod 555 $local_dest + else + warning "$local_dest does not exist!" 0 + fi + else + verbose "$local_dest is OK" + fi + + if [ ! -h $local_symlink ]; then + if [ $mode = "fix" ]; then + verbose "Setting up $local_symlink" + ln -s $local_dest $local_symlink + else + warning "$local_symlink does not exist!" 0 + fi + else + verbose "$local_symlink is OK" + fi + + if [ ! -d /etc/Startup ]; then + if [ $mode = "fix" ]; then + verbose "Creating /etc/Startup directory" + mkdir /etc/Startup + chown root:adm /etc/Startup + # Note: Security would be better if this was 775... + chmod 777 /etc/Startup + else + warning "/etc/Startup does not exist!" 0 + fi + else + verbose "/etc/Startup is OK" + fi + + start_views_src=$ADM_PATH/clearcase/start_views + start_views_dest=/etc/Startup/start_views + + if [ ! -x $start_views_dest ]; then + if [ $mode = "fix" ]; then + verbose "Creating $start_views_dest" + cp "$start_views_src" $start_views_dest + chown root:adm $start_views_dest + chmod 555 $start_views_dest + else + warning "$start_views_dest does not exist!" 0 + fi + else + verbose "$start_views_dest is OK" + fi + + views_to_start_src=$ADM_PATH/clearcase/views_to_start + views_to_start_dest=/etc/views_to_start + + if [ ! -f $views_to_start_dest ]; then + if [ $mode = "fix" ]; then + verbose "Creating $views_to_start_dest" + cp "$views_to_start_src" $views_to_start_dest + chown root:adm $views_to_start_dest + chmod 555 $views_to_start_dest + else + warning "$views_to_start_dest does not exist!" 0 + fi + else + verbose "$views_to_start_dest is OK" + fi + + debug "EXIT setup_rcfiles" +} # setup_rcfiles + +function setup_root_rhosts { + debug "ENTER setup_root_rhosts" + root_rhosts_src="$ADM_PATH/etc/root_rhosts" + root_rhosts_dest="/.rhosts" + + if [ ! -f $root_rhosts_dest ]; then + if [ $mode = "fix" ]; then + verbose "Creating $root_rhosts_dest" + cp "$root_rhosts_src" $root_rhosts_dest + chown root:adm $root_rhosts_dest + chmod 400 $root_rhosts_dest + else + warning "$root_rhosts_dest does not exist!" 0 + fi + else + if is_root; then + if ! cmp -s $root_rhosts_src $root_rhosts_dest; then + if [ $mode = "fix" ]; then + verbose "Updating $root_rhosts_dest" + cp "$root_rhosts_src" $root_rhosts_dest + chown root:adm $root_rhosts_dest + chmod 400 $root_rhosts_dest + else + warning "Contents of $root_rhosts_dest is non standard!" 0 + fi + fi + else + verbose "$root_rhosts_dest is present" + verbose "Contents not check since you're not running as root" + fi + fi + + debug "EXIT setup_root_rhosts" +} # setup_root_rhosts + +function setup_root_profile { + debug "ENTER setup_root_profile" + if [ "$VENDOR" = "HP" ]; then + debug "RETURN setup_root_profile (Skip HP machines)" + return + fi + root_profile_src="$ADM_PATH/etc/root_profile" + root_profile_dest="/.profile" + + if [ ! -f $root_profile_dest ]; then + if [ $mode = "fix" ]; then + verbose "Creating $root_profile_dest" + cp "$root_profile_src" $root_profile_dest + chown root:adm $root_profile_dest + chmod 444 $root_profile_dest + else + warning "$root_profile_dest does not exist!" 0 + fi + else + if is_root; then + if ! cmp -s $root_profile_src $root_profile_dest; then + if [ $mode = "fix" ]; then + verbose "Updating $root_profile_dest" + cp "$root_profile_src" $root_profile_dest + chown root:adm $root_profile_dest + chmod 400 $root_profile_dest + else + warning "Contents of $root_profile_dest is non standard!" 0 + fi + fi + else + verbose "$root_profile_dest is present" + verbose "Contents not check since you're not running as root" + fi + fi + + debug "EXIT setup_root_profile" +} # setup_root_rhosts + +# Get parameters +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -v|-verbose) + verbose=yes + ;; + + -d|-debug) + debug=yes + ;; + + -f|-fix) + mode=fix + ;; + + -c|-check) + mode=check + ;; + + *) + usage "Unrecognized parameter $1" + ;; + esac + shift +done + +if [ "$mode" = "fix" ]; then + if ! is_root; then + error "Must be root to execute this command in fix mode!" 2 + fi +fi + +verbose "Starting $me..." +setup_symlinks +check_nsswitch_conf +check_automount_maps +setup_rcfiles +setup_root_rhosts +setup_root_profile +verbose "$me completed" diff --git a/clients/HP/bin/cpucount b/clients/HP/bin/cpucount new file mode 100644 index 0000000..fab8e65 --- /dev/null +++ b/clients/HP/bin/cpucount @@ -0,0 +1,34 @@ +#!/usr/bin/ksh + +cpus=1 + +top -d 1 > /tmp/jwhzxz + +grep -q "\[B 1" /tmp/jwhzxz +if [ $? -eq 0 ]; then + cpus=2 +else + grep -q "\[B 2" /tmp/jwhzxz + if [ $? -eq 0 ]; then + cpus=3 + else + grep -q "\[B 3" /tmp/jwhzxz + if [ $? -eq 0 ]; then + cpus=4 + else + grep -q "\[B 4" /tmp/jwhzxz + if [ $? -eq 0 ]; then + cpus=5 + else + grep -q "\[B 5" /tmp/jwhzxz + if [ $? -eq 0 ]; then + cpus=6 + fi + fi + fi + fi +fi + +rm /tmp/jwhzxz + +echo $cpus diff --git a/clients/HP/bin/cygwin_setup b/clients/HP/bin/cygwin_setup new file mode 100644 index 0000000..d013848 --- /dev/null +++ b/clients/HP/bin/cygwin_setup @@ -0,0 +1,241 @@ +#!/bin/bash +################################################################################ +# +# File: cygwin_setup +# Description: This script will perform additional setup to configure the +# local machine into the cygwin enviornment for Salira +# Author: Andrew@DeFaria.com +# Created: Fri Oct 5 15:30:16 2001 +# Modified: +# Language: Bash Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Global variables +commonserver=sonscentral +commonarea=common +adm=//$commonserver/$commonarea/adm +homeserver=sonscentral +homeshare=users +ccserver=sons-clearcase +anonymous_ftp_server=sons-clearcase +viewshare=views +printerserver=sons-mrp +printers="\ + LJ4050PCL6\ + LJ45500-Color\ + LJ8150\ +" +defaultprinter=LJ8150 + +# Current machine's OS. +OS=$(uname -s | cut -f2 -d-) + +# Current machine's hostname +hostname=$(echo $(hostname) | tr [:upper:] [:lower:]) + +# Setup standard mounts +# +# Home directory +echo "Step 1 of 10: Setting up /home mount point" +mount -tsf //$homeserver/$homeshare /home + +# Clearcase views +echo "Step 2 of 10: Setting up /view mount point" +if [ $hostname = $ccserver ]; then + mount -tsf C:/ClearCaseStorage/Views /view +else + mount -tsf //$ccserver/$viewshare /view +fi + +# Set cygdrive prefix to /dev +echo "Step 3 of 10: Setting cygdrive-prefix to /dev" +mount -s --change-cygdrive-prefix /dev + +# Remove user level cygdrive-prefix (Need to do this with regedit +regedit /s \\\\$commonserver\\$commonarea\\FixCygwin.reg + +# Link passwd file +echo "Step 4 of 10: Create common password file" +if [ ! -f /etc/passwd.local ]; then + if [ ! -L /etc/passwd ]; then + cp /etc/passwd /etc/passwd.local + fi +fi + +if [ ! -L /etc/passwd ]; then + if [ "$OS" != "4.0" ]; then + rm /etc/passwd + ln -s //$commonserver/$commonarea/passwd /etc/passwd + else + cp //$commonserver/$commonarea/passwd /etc/passwd + fi +else + if [ "$OS" = "4.0" ]; then + # Fix up NT 4.0 machines (they don't like symlinked /etc/passwd files!) + rm /etc/passwd + cp //$commonserver/$commonarea/passwd /etc/passwd + fi +fi + +# Link group file +echo "Step 5 of 10: Create common group file" +if [ ! -f /etc/group.local ]; then + if [ ! -L /etc/group ]; then + cp /etc/group /etc/group.local + fi +fi + +if [ ! -L /etc/group ]; then + rm /etc/group + ln -s //$commonserver/$commonarea/group /etc/group +fi + +# Link /etc/profile +echo "Step 6 of 10: Linking /etc/profile to common profile file" +if [ ! -f /etc/profile.orig ]; then + if [ ! -L /etc/profile ]; then + cp /etc/profile /etc/profile.orig + fi +fi + +if [ ! -L /etc/profile ]; then + rm /etc/profile + ln -s //$commonserver/$commonarea/profile /etc/profile +fi + +# Setup printer mount +echo "Step 7 of 10: Setting up printers" +for printer in $printers; do + mount -bsf //$printerserver/$printer /dev/$printer +done + +# Mount default printer +mount -bsf //$printerserver/$defaultprinter /dev/lp + +# Install internet services +echo "Step 8 of 10: Installing internet services" + +# First save any pre-existing /etc/motd +if [ -f /etc/motd ]; then + cp /etc/motd /etc/motd.$$ +fi + +rm -f /etc/ftpusers /etc/ftpwelcome /etc/inetd.conf /etc/motd /etc/shells +iu-config > /dev/null + +# In order to allow anonymous ftp access we need to clear /etc/ftpusers. +# Do this only for the $anonymous_ftp_server for now +if [ $hostname = $anonymous_ftp_server ]; then + cat /dev/null > /etc/ftpusers +fi + +# Now replace that saved /etc/motd if it existed, otherwise remove the boring +# /etc/motd that iu-config creates. First check to see if the user has a +# personalized /etc/motd in /etc/motd.save +if [ -f /etc/motd.save ]; then + # User had a personalized motd so move it into place and remove any prior + # copies + mv /etc/motd.save /etc/motd + rm -f /etc/motd.$$ +elif [ -f /etc/motd.$$ ]; then + # Reinstall previous motd + # First update uname -a line + uname -a > /etc/motd + + # Remove old uname -a line if present + grep -ve "^cygwin" /etc/motd.$$ >> /etc/motd.$$ + + # Cleanup + rm -f /etc/motd.$$ +else + # No saved motd or previous motd. Remove /etc/motd which will cause us + # to prompt for the information later. + rm /etc/motd +fi + +# Need to hardlink /usr/bin/cygwin1.dll & /usr/sbin/cygwin1.dll +# 12/17/2001: Stopped hardlinking cygwin1.dll. Enforcing having Windows system +# environment variables instead. For this we need Cygwin's bin in the path. +# User should also set CYGWIN=ntsec in a Windows system environment variable. +if [ -f /usr/sbin/cygwin1.dll ]; then + rm -f /usr/sbin/cygwin1.dll + #ln /usr/bin/cygwin1.dll /usr/sbin/cygwin1.dll + echo "Warning: Please make sure that you have a Windows *SYSTEM* environment" + echo " variable named CYGWIN set to the value of \"ntsec\" and that" + echo " you have \bin inserted into the Windows *SYSTEM*" + echo " environment variable named PATH" +fi + +# Set up anonymous ftp iff we are on the $anonymous_ftp_server +if [ $hostname = $anonymous_ftp_server ]; then + # Toggle on write access to ~ftp/bin + chmod +w ~ftp/bin + + # Remove old copies of ls and cygwin1.dll + rm -f ~ftp/bin/ls.exe + rm -f ~ftp/bin/cygwin1.dll + + # Install new copies (Note hardlinks will not work here since ~ftp/bin is + # on another file system. Doing an ln simply does a copy anyway) + # 12/17/2001: Skipping copying of cygwin1.dll as noted above + cp /bin/cygwin1.dll ~ftp/bin/cygwin1.dll + cp /bin/ls.exe ~ftp/bin/ls.exe + + # Set security + chmod 555 ~ftp/bin/cygwin1.dll + chmod 111 ~ftp/bin/ls.exe + chown Administrator ~ftp/bin/cygwin1.dll + chown Administrator ~ftp/bin/ls.exe + chmod -w ~ftp/bin +fi + +# Install inetd as a service +/usr/sbin/inetd --install-as-service + +# Start inetd service +inetd_started=$(net start | grep -i inetd) + +if [ -z "$inetd_started" ]; then + net start inetd +fi + +# Setup SMTP +$adm/bin/setup_ssmtp + +# Setup cron +$adm/bin/setup_cron + +# Create /etc/motd +echo "Step 9 of 10: Gathering machine specific information" +if [ ! -f /etc/motd ]; then + $adm/bin/make_motd + made_motd=true +else + echo "Skipped: Machine info already gathered" +fi + +# Fixup /etc/ftpwelcome +host=$(hostname | tr [:upper:] [:lower:]) +echo "Welcome to $host's ftp service" > /etc/ftpwelcome + +# Update machines file +echo "Step 10 of 10: Registering this machine (This takes a few seconds)" +if [ ! -z "$made_motd" ]; then + $adm/bin/update_machine_info +else + echo "Skipped: Machine already registered" +fi + +# Sneaky other fixes... +# Link /bin/more.exe -> /bin/less.exe +if [ ! -L /bin/more.exe ]; then + ln -s /bin/less.exe /bin/more.exe +fi + +# Finished +echo "Done" diff --git a/clients/HP/bin/daily b/clients/HP/bin/daily new file mode 100644 index 0000000..ae93e19 --- /dev/null +++ b/clients/HP/bin/daily @@ -0,0 +1,151 @@ +#!/bin/ksh +################################################################################ +# +# File: daily +# Description: This is the daily cronjob for root +# Author: Andrew@DeFaria.com +# Created: Wed Jul 21 12:12:28 PDT 1999 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +# Add $adm_base/bin and $adm_base/clearcase PATH +export PATH=$adm_base/bin:$adm_base/clearcase:$PATH + +# Source in tmpfiles function +tmpprefix=${TMPDIR:-/tmp}/$me.$$ +tmpfile=$tmpprefix +. $adm_fpath/tmpfiles +arm_trap + +# Where logs are kept +logs=$adm_host/logs + +# Define admin_host. Admin_host is the machine where checks for the network +# as a whole are run (such as check_view_storage) +admin_host=dreamcicle # For now... + +verbose= +debug= + +function usage { + display "$me [-v|verbose] [-d|debug] [-u|usage] [-n|notify ]" + display " -v|verbose: Turns on verbose mode" + display " -d|debug: Turns on debug mode" + display " -u|usage: Print this usage message\n" + display " -n|notify: Who to notify of problems (default root)" + + error "$1" 1 +} # usage + +lab_admin=cdsadmin # For now +local_admin=cdsadmin + +function notify { + debug "ENTER notify" + who=$1 + logfile=$2 + + cat > $tmpfile <> $tmpfile + + mailx -s "Notice: $me cronjob discovered the following problems" $who < +$tmpfile + debug "EXIT notify" +} # notify + +# Get parameters +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -v|-verbose) + verbose=yes + ;; + + -d|-debug) + debug=yes + ;; + + -n|-notify) + shift + if [ $# -lt 1 ]; then + error "Notify email address was unspecified!" 1 + fi + local_admin="$1" + ;; + + *) + usage "Unrecognized parameter $1" + ;; + esac + shift +done + +## Main +# Some common portions of log filenames: +host=$(uname -n) # System's name +date=$(date +%d) # Note we keep one month of rolling logs + +if [ "$host" = "$admin_host" ]; then + network_diskspace_log=$logs/network.diskspace.$date.log + verbose "Diskspace Report -> $network_diskspace_log" + diskspace -network > $network_diskspace_log 2>&1 + + if [ -s $network_diskspace_log ]; then + notify $lab_admin $network_diskspace_log + fi + + view_storage_log=$logs/viewstorage.$date.log + verbose "View Storage Report -> $view_storage_log" + check_view_storage > $view_storage_log 2>&1 + + if [ -s $view_storage_log ]; then + notify $lab_admin $view_storage_log + fi + + # Produce a viewspace report for all production view servers + viewservers="cds-sundev-rem canon" + + for viewserver in $viewservers; do + viewspace_log=$logs/$viewserver.viewspace.$date.log + verbose "Viewspace Report for $viewserver -> $viewspace_log" + viewspace -host $viewserver > $viewspace_log + done +fi + +# Checks run on all machines +local_diskspace_log=$logs/$host.diskspace.$date.log +verbose "Diskspace Report -> $local_diskspace_log" +diskspace -local > $local_diskspace_log 2>&1 + +if [ -s $local_diskspace_log ]; then + notify local_admin $local_diskspace_log +fi + +machine_configuration_log=$logs/$host.machine_configuration.$date.log +verbose "Machine Configuration Report -> $machine_configuration_log" 2>&1 & +configure_machine -f > $machine_configuraton_log 2>&1 + +if [ -s $machine_configuration_log ]; then + notify local_admin $machine_configuration_log +fi diff --git a/clients/HP/bin/desktopmach b/clients/HP/bin/desktopmach new file mode 100644 index 0000000..f128765 --- /dev/null +++ b/clients/HP/bin/desktopmach @@ -0,0 +1,60 @@ +#!/bin/ksh +################################################################################ +# +# File: desktopmach +# Description: A script to execute a command on all desktop class machines +# Author: Andrew@DeFaria.com +# Created: Thu May 11 11:08:24 PDT 2000 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +# Set machines +machines=${machines:-$adm_base/data/machines} + +if [ "$1" = "-f" ]; then + shift + machines="$1" + shift +fi + +PATH=/adm/bin:$PATH + +if [ "$1" = "-r" ]; then + root=yes + shift +fi + +for desktop_machine in $(grep -ve ^# $machines | grep :Desktop: | cut -d: +-f1); do + # Execute command. Note if no command is given then the effect is to + # rlogin to each machine. + print "$desktop_machine:$@" + if [ $# -gt 0 ]; then + if [ -z "$root" ]; then + remsh $desktop_machine -n "$@" + else + root remsh $desktop_machine -n "$@" + fi + else + if [ -z "$root" ]; then + remsh $desktop_machine + else + root remsh $desktop_machine + fi + fi +done diff --git a/clients/HP/bin/diskspace b/clients/HP/bin/diskspace new file mode 100644 index 0000000..3e2f3e2 --- /dev/null +++ b/clients/HP/bin/diskspace @@ -0,0 +1,112 @@ +#!/usr/bin/perl +################################################################################ +# +# File: diskspace +# Description: Check filesystems to see if they are becoming too full +# Author: Andrew@DeFaria.com +# Created: Fri Mar 12 10:17:44 PST 2004 +# Language: Perl +# Modifications: +# +# (c) Copyright 2004, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +use strict; +use warnings; +use File::Spec; + +my ($me, $abs_path, $lib_path, $bin_path, $log_path); + +BEGIN { + # Extract relative path and basename from script name. + $0 =~ /(.*)[\/\\](.*)/; + + $abs_path = (!defined $1) ? "." : File::Spec->rel2abs ($1); + $me = (!defined $2) ? $0 : $2; + + # Setup paths + $bin_path = "$abs_path"; + $lib_path = "$abs_path/../../lib"; + $log_path = "$abs_path/../../log"; + + # Add the appropriate path to our modules to @INC array. + unshift (@INC, "$lib_path"); +} # BEGIN + +use Display; + +my $threshold = 90; + +sub Usage { + my $msg = shift; + + display "ERROR: $msg\n" if defined $msg; + + display "$me\t[-v] [-d] [-u] [ -n | -l ]"; + display "\t-v\tTurn on verbose mode"; + display "\t-d\tTurn on debug mode"; + display "\t-u\tThis usage message"; + display "\t-t\tThreshold (0-100)"; + + exit 1; +} # Usage + +sub CheckFilesystemSpace { + my $filesystem = shift; + + # Isolate the disk usage line + my @diskusage = `df -k $filesystem`; + + # Get fields + $_ = $diskusage [1]; + my ($fs, $blocks, $used, $available, $used_percent, $mounted_on) = split; + + if ($used_percent =~ /(\d+)%/) { + $used_percent = $1; + } # if + + $available = sprintf ("%.3f", $available / 1024); + + # Check if over threshold and report + if ($used_percent <= $threshold ) { + verbose "$mounted_on is $used_percent% full - $available Megs left"; + } else { + warning "$mounted_on is $used_percent% full - $available Megs left"; + } # if +} # CheckFilesystemSpace + +sub CheckLocalFilesystems { + my @local_filesystems = `df -k`; + + @local_filesystems = grep {/^\/dev/} @local_filesystems; + + foreach (@local_filesystems) { + my ($fs, $blocks, $used, $available, $used_percent, $mounted_on) = split; + CheckFilesystemSpace $mounted_on; + } # foreach +} # CheckLocalFilesystems + +# Get parameters +while ($ARGV [0]) { + if ($ARGV [0] eq "-v") { + set_verbose; + } elsif ($ARGV [0] eq "-d") { + set_debug; + } elsif ($ARGV [0] eq "-t") { + shift (@ARGV); + if (!$ARGV [0]) { + Usage "Must specify threshold after -t"; + } else { + $threshold = $ARGV [0]; + } # if + } elsif ($ARGV [0] eq "-u") { + Usage; + } else { + Usage "Unknown argument found: " . $ARGV [0]; + } # if + + shift (@ARGV); +} # while + +debug "Theshold: $threshold"; +CheckLocalFilesystems; diff --git a/clients/HP/bin/display_path b/clients/HP/bin/display_path new file mode 100644 index 0000000..df825dc --- /dev/null +++ b/clients/HP/bin/display_path @@ -0,0 +1,32 @@ +#!/bin/bash +################################################################################ +# +# File: display_path +# Description: Displays the components in PATH +# Author: Andrew@DeFaria.com +# Language: Bash Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved. +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +declare -i i=0 + +for path_component in $(echo $PATH | tr ":" "\n"); do + let i=i+1 + if [ $i -eq 1 ]; then + display "PATH consists of the following components:\n" + fi + display "\t$i) $path_component" +done | more diff --git a/clients/HP/bin/dum b/clients/HP/bin/dum new file mode 100644 index 0000000..48e445e --- /dev/null +++ b/clients/HP/bin/dum @@ -0,0 +1,19 @@ +#!/bin/bash +################################################################################ +# +# File: dum +# Description: Outputs disk usage in Megabytes (what a concept! :-) +# Author: Andrew@DeFaria.com +# Created: Mon Nov 13 16:14:30 1995 +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +lines=${LINES:-24} +du "$@" 2> /dev/null | + sort -nr | + awk '{ + filename=substr ($0, length ($1) + 2, (length ($0) - length ($1)) - 1); + printf "%.3f%s%s\n", $1/1024, "\t", filename + }' | + head -$lines diff --git a/clients/HP/bin/fix_automounts b/clients/HP/bin/fix_automounts new file mode 100644 index 0000000..ec1ac67 --- /dev/null +++ b/clients/HP/bin/fix_automounts @@ -0,0 +1,91 @@ +#!/bin/ksh +################################################################################ +# +# File: fix_automounts +# Description: Remounts exported file systems from a machine +# Author: Andrew DeFaria (defaria@cup.hp.com) +# Language: Korn Shell +# Created: Tue Apr 21 11:59:57 PDT 1998 +# Status: Experimental (Do Not Distribute) +# Modifications: Removed the grep "(everyone)". In the past file systems were +# exported to everyone and that is no longer the case. This does +# mean that this script might attempt to mount things it can't +# but there is no easy way to ascertain if the current machine +# is allowed to mount given showmount -e output, especially if +# that output is a netgroup. I could check this via ypmatch +# but that'll take yet more smarts... +# Andrew@DeFaria.com +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +if [ $(id -u) -ne 0 ]; then + print -u2 "$me: Error: You must be root to execute this command!" + exit 1 +fi + +if [[ "$OS" = "10" ]] ; then + mount=/usr/sbin/mount +else + mount=/etc/mount +fi + +if [ $# -lt 1 ]; then + print -u2 "Usage: [ ]" + exit 1 +fi + +function remount_filesystem { + machine=$1 + mount_directory=$2 + mount_over_directory=/tmp_mnt/net/$machine$2 + if [ ! -d $mount_over_directory ]; then + print Making $mount_over_directory + mkdir -p $mount_over_directory + fi + + if [ ! -d $mount_over_directory/lost+found ]; then + print Mounting $machine:$mount_directory to $mount_over_directory + $mount $machine:$mount_directory $mount_over_directory + status=$? + if [ $status -ne 0 ]; then + print -u2 "Warning: Unable to mount $machine:$mount_directory +$mount_over_directory (Status: $?)" + fi + fi +} # remount_filesystem + +function kick_automounter { + automount_pid=$(ps -ef | grep automount | grep -v "grep automount" | grep -v "fix_automounts" | awk '{print $2}') + + print Kicking automounter \($automount_pid\) + kill -HUP $automount_pid +} # kick_automounter + +if [ "$OS" = "10" ]; then + showmount=/usr/sbin/showmount +else + showmount=/usr/etc/showmount +fi + +for remote in "$@"; do + exported_filesystems=$($showmount -e $remote | grep -v "export list" | + cut -f1 -d' ') + for filesystem in $exported_filesystems; do + remount_filesystem $remote $filesystem + done +done + +kick_automounter diff --git a/clients/HP/bin/fix_startup b/clients/HP/bin/fix_startup new file mode 100644 index 0000000..41722b2 --- /dev/null +++ b/clients/HP/bin/fix_startup @@ -0,0 +1,53 @@ +#!/bin/ksh +################################################################################ +# +# File: fixr_startup +# Description: This script will fix the local startup scripts. Thist is done +# by moving them from /sbin/rc2.d to /sbin/rc3.d where they +# really belong. +# Author: Andrew DeFaria (defaria@cup.hp.com) +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# First source /app/appserver +if [ -x /app/appserver ]; then + . /app/appserver +fi + +if [ $(id -u) -ne 0 ]; then + print -u2 "Error: You must be root to execute this command!" + exit 1 +fi + +if [ "$OS" = "09" ]; then + print -u2 "Error: $(basename $0) does not run on 9.x!" + exit 1 +fi + +moved_a_file=no + +if [ -x /sbin/rc2.d/S900local ]; then + print "Moving /sbin/rc2.d/S900local \-> /sbin/rc3.d/S900local" + mv /sbin/rc2.d/S900local /sbin/rc3.d/S900local + moved_a_file=yes +fi + +if [ -x /sbin/rc2.d/S920start_views ]; then + print "Moving /sbin/rc2.d/S920start_views \-> /sbin/rc3.d/S770start_views" + mv /sbin/rc2.d/S920start_views /sbin/rc3.d/S770start_views + moved_a_file=yes +fi + +if [ -x /sbin/rc2.d/S910mount_additional_vobs ]; then + print "Moving /sbin/rc2.d/S910mount_additional_vobs \-> /sbin/rc3.d/S775mount_additional_vobs" + mv /sbin/rc2.d/S910mount_additional_vobs /sbin/rc3.d/S775mount_additional_vobs + moved_a_file=yes +fi + +if [ "$moved_a_file" = "no" ]; then + print "Startup scripts already in their proper places" +else + print "Fixed startup scripts" +fi diff --git a/clients/HP/bin/fixlocalaliases b/clients/HP/bin/fixlocalaliases new file mode 100644 index 0000000..e81a80a --- /dev/null +++ b/clients/HP/bin/fixlocalaliases @@ -0,0 +1,17 @@ +#!/bin/ksh + +echo "Now on `uname -n`" +grep "root: cll-support" /etc/mail/aliases >/dev/null 2>&1 +if [ $? -ne 0 ]; then + echo " No change" + exit 0; +fi + +echo " Changing aliases.local" +sed -e "s/root\: cll-support/root\: cll-root/" \ + -e "s/paladm\: cll-support/paladm\: cll-paladm/" \ + < /etc/mail/aliases.local > /tmp/aliases.local.new +mv /etc/mail/aliases.local /etc/mail/aliases.local.`date +"%m%d%y_%H%M%S"` +mv /tmp/aliases.local.new /etc/mail/aliases.local +chmod 644 /etc/mail/aliases.local +chown root:root /etc/mail/aliases.local diff --git a/clients/HP/bin/fixntp b/clients/HP/bin/fixntp new file mode 100644 index 0000000..1ed4538 --- /dev/null +++ b/clients/HP/bin/fixntp @@ -0,0 +1,43 @@ +#!/bin/ksh +################################################################################ +# +# File: fixntp +# Description: This script will fix /etc/rc.config.d/netdaemons to set +# NTPDATE_SERVER to cupertino.ntp.hp.com +# Author: Andrew DeFaria (defaria@cup.hp.com) +# Language: Korn Shell +# Modified: +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# First source /app/appserver +if [ -x /app/appserver ]; then + . /app/appserver +fi + +if [ $(id -u) -ne 0 ]; then + print -u2 "Error: You must be root to execute this command!" + exit 1 +fi + +if [ "$OS" = "09" ]; then + print -u2 "Error: $(basename $0) does not run on 9.x!" + exit 1 +fi + +netdaemons=/etc/rc.config.d/netdaemons +netdaemons_new=/etc/rc.config.d/netdaemons.$$ + +sed 's/NTPDATE_SERVER=$/NTPDATE_SERVER=cupertino.ntp.hp.com/' \ + $netdaemons > $netdaemons_new + +if cmp -s $netdaemons $netdaemons_new; then + rm -f $netdaemons_new + print "NTPDATE_SERVER already set properly - No fix needed!" +else + mv $netdaemons_new $netdaemons + chmod 555 $netdaemons + chown bin:bin $netdaemons + print "Changed NTPDATE_SERVER to be set properly" +fi diff --git a/clients/HP/bin/fixrhosts b/clients/HP/bin/fixrhosts new file mode 100644 index 0000000..120ac6e --- /dev/null +++ b/clients/HP/bin/fixrhosts @@ -0,0 +1,65 @@ +#!/bin/ksh +################################################################################ +# +# File: fixrhosts +# Description: Generates a new ~/.rhosts file +# Author: Andrew@DeFaria.com +# Created: Fri Apr 30 14:13:56 PDT 1999 +# Modifications: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +# Source in tmpfiles function +tmpprefix=${TMPDIR:-/tmp}/$me.$$ +. $adm_fpath/tmpfiles +trap cleanup INT EXIT ERR + +# Check if root +if is_root; then + error "You should not run the script as root!" 1 +fi + +rhosts=$HOME/.rhosts +rhosts_loc=$HOME/.rhosts.loc + +# Generate new $rhosts + +if [ -f $rhosts_loc ]; then + cp $rhosts_loc $rhosts +else + rm -f $rhosts +fi + +# Add netgroup +print "+@all-machines $LOGNAME" >> .rhosts + +# Insure proper permissions +chmod 600 $rhosts + +# Tell user of this scripts demise +display "\t\t\t*** NOTICE ***" +display +display "Since the lab now users NIS we are employing a different solution" +display "to the issue of providing passwordless logins to sets of machines" +display "via rlogin/remsh. This solution adds the following to $rhosts:" +display +display "\t+@all-machines $LOGNAME" +display +display "Thereafter running $me is unnecessary." +display +display "I'd like to take this opportunity to thank you for your support" +display "of $me! :-) " diff --git a/clients/HP/bin/fk1 b/clients/HP/bin/fk1 new file mode 100644 index 0000000..3ded632 --- /dev/null +++ b/clients/HP/bin/fk1 @@ -0,0 +1,98 @@ +#!/bin/ksh +################################################################################ +# +# File: .tep +# RCS: $Header: .tep,v 1.5 97/10/05 22:31:46 defaria Exp $ +# Description: Wrapper script to set function keys for the TEP Console +# Concentrators. +# Author: Andrew DeFaria, California Language Labs +# Created: Thu Jun 6 08:31:57 PDT 1996 +# Modified: Thu Jun 6 08:32:13 PDT 1996 (Andrew DeFaria) defaria@spock +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) +export esc=$(print "\033") +export cr=$(print "\015") + +if [ "$TERM" = "hpterm" \ + -o "$TERM" = "hp" \ + -o "$TERM" = "2394" \ + -o "$TERM" = "70096" ]; then + # Turn: + # . Enq/Ack: No + # . RecvPace: Xon/Xoff + # . InhHndShk (G): Yes + # . Inh DC2 (H): Yes + print "${esc}&q0n1h${esc}&s1g1H\c" + + if [ "$me" = fk1 ]; then + print "\ +${esc}&f1k2a16d7L Young hpcleareQQQQT1${cr}\ +${esc}&f2k2a16d7L Loomis hpclsv1 QQQQT2${cr}\ +${esc}&f3k2a16d7L hpcll237QQQQT3${cr}\ +${esc}&f4k2a16d7L Stablerhpclear1QQQQT4${cr}\ +${esc}&f5k2a16d7L Unitas hpclear3QQQQT5${cr}\ +${esc}&f6k2a16d7L Griese hpclear5QQQQT6${cr}\ +${esc}&f7k2a16d7L Simms hpclear7QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk2 ]; then + print "\ +${esc}&f1k2a16d7LDynamitehpclldynQQQQT1${cr}\ +${esc}&f2k2a16d7L Mobius hpclang6QQQQT2${cr}\ +${esc}&f3k2a16d7L Starr hpclearnQQQQT3${cr}\ +${esc}&f4k2a16d7L hpcll208QQQQT4${cr}\ +${esc}&f5k2a16d7LTarkentnhpclear9QQQQT5${cr}\ +${esc}&f6k2a16d7L Veil QQQQT6${cr}\ +${esc}&f7k2a16d7L Kilmer hpclearkQQQQT7${cr}\ +${esc}&f8k2a16d7L hpcll207QQQQT8${cr}\c" + elif [ $me = fk3 ]; then + print "\ +${esc}&f1k2a16d7LDuchess QQQQT1${cr}\ +${esc}&f2k2a16d7LCatbert QQQQT2${cr}\ +${esc}&f3k2a16d7L Nala QQQQT3${cr}\ +${esc}&f4k2a16d7LPywacket QQQQT4${cr}\ +${esc}&f5k2a16d7L Alley QQQQT5${cr}\ +${esc}&f6k2a16d7L QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk4 ]; then + print "\ +${esc}&f1k2a16d7L Wampus QQQQT1${cr}\ +${esc}&f2k2a16d7Lcllvob01 QQQQT2${cr}\ +${esc}&f3k2a16d7Lcllvob02 QQQQT3${cr}\ +${esc}&f4k2a16d7Lcllvob03 QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllvob04 QQQQT5${cr}\ +${esc}&f6k2a16d7L QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk5 ]; then + print "\ +${esc}&f1k2a16d7Lcllvob05 QQQQT1${cr}\ +${esc}&f2k2a16d7Lcllvob06 QQQQT2${cr}\ +${esc}&f3k2a16d7Lcllvob07 QQQQT3${cr}\ +${esc}&f4k2a16d7Lcllvob08 QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllvob09 QQQQT5${cr}\ +${esc}&f6k2a16d7Lcllvob10 QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk6 ]; then + print "\ +${esc}&f1k2a16d7L Gideon QQQQT1${cr}\ +${esc}&f2k2a16d7L Oliver QQQQT2${cr}\ +${esc}&f3k2a16d7L Dinah QQQQT3${cr}\ +${esc}&f4k2a16d7LCheshire QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllbld01 QQQQT5${cr}\ +${esc}&f6k2a16d7Lcllbld02 QQQQT6${cr}\ +${esc}&f7k2a16d7Lcllbld03 QQQQT7${cr}\ +${esc}&f8k2a16d7Lhpcll321 QQQQT8${cr}\c" + fi + + # Turn on Function keys to see changes + print "${esc}&jB\c" +else + print -u2 "Sorry but the terminal type $TERM, is not supported" + exit 1 +fi diff --git a/clients/HP/bin/fk2 b/clients/HP/bin/fk2 new file mode 100644 index 0000000..3ded632 --- /dev/null +++ b/clients/HP/bin/fk2 @@ -0,0 +1,98 @@ +#!/bin/ksh +################################################################################ +# +# File: .tep +# RCS: $Header: .tep,v 1.5 97/10/05 22:31:46 defaria Exp $ +# Description: Wrapper script to set function keys for the TEP Console +# Concentrators. +# Author: Andrew DeFaria, California Language Labs +# Created: Thu Jun 6 08:31:57 PDT 1996 +# Modified: Thu Jun 6 08:32:13 PDT 1996 (Andrew DeFaria) defaria@spock +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) +export esc=$(print "\033") +export cr=$(print "\015") + +if [ "$TERM" = "hpterm" \ + -o "$TERM" = "hp" \ + -o "$TERM" = "2394" \ + -o "$TERM" = "70096" ]; then + # Turn: + # . Enq/Ack: No + # . RecvPace: Xon/Xoff + # . InhHndShk (G): Yes + # . Inh DC2 (H): Yes + print "${esc}&q0n1h${esc}&s1g1H\c" + + if [ "$me" = fk1 ]; then + print "\ +${esc}&f1k2a16d7L Young hpcleareQQQQT1${cr}\ +${esc}&f2k2a16d7L Loomis hpclsv1 QQQQT2${cr}\ +${esc}&f3k2a16d7L hpcll237QQQQT3${cr}\ +${esc}&f4k2a16d7L Stablerhpclear1QQQQT4${cr}\ +${esc}&f5k2a16d7L Unitas hpclear3QQQQT5${cr}\ +${esc}&f6k2a16d7L Griese hpclear5QQQQT6${cr}\ +${esc}&f7k2a16d7L Simms hpclear7QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk2 ]; then + print "\ +${esc}&f1k2a16d7LDynamitehpclldynQQQQT1${cr}\ +${esc}&f2k2a16d7L Mobius hpclang6QQQQT2${cr}\ +${esc}&f3k2a16d7L Starr hpclearnQQQQT3${cr}\ +${esc}&f4k2a16d7L hpcll208QQQQT4${cr}\ +${esc}&f5k2a16d7LTarkentnhpclear9QQQQT5${cr}\ +${esc}&f6k2a16d7L Veil QQQQT6${cr}\ +${esc}&f7k2a16d7L Kilmer hpclearkQQQQT7${cr}\ +${esc}&f8k2a16d7L hpcll207QQQQT8${cr}\c" + elif [ $me = fk3 ]; then + print "\ +${esc}&f1k2a16d7LDuchess QQQQT1${cr}\ +${esc}&f2k2a16d7LCatbert QQQQT2${cr}\ +${esc}&f3k2a16d7L Nala QQQQT3${cr}\ +${esc}&f4k2a16d7LPywacket QQQQT4${cr}\ +${esc}&f5k2a16d7L Alley QQQQT5${cr}\ +${esc}&f6k2a16d7L QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk4 ]; then + print "\ +${esc}&f1k2a16d7L Wampus QQQQT1${cr}\ +${esc}&f2k2a16d7Lcllvob01 QQQQT2${cr}\ +${esc}&f3k2a16d7Lcllvob02 QQQQT3${cr}\ +${esc}&f4k2a16d7Lcllvob03 QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllvob04 QQQQT5${cr}\ +${esc}&f6k2a16d7L QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk5 ]; then + print "\ +${esc}&f1k2a16d7Lcllvob05 QQQQT1${cr}\ +${esc}&f2k2a16d7Lcllvob06 QQQQT2${cr}\ +${esc}&f3k2a16d7Lcllvob07 QQQQT3${cr}\ +${esc}&f4k2a16d7Lcllvob08 QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllvob09 QQQQT5${cr}\ +${esc}&f6k2a16d7Lcllvob10 QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk6 ]; then + print "\ +${esc}&f1k2a16d7L Gideon QQQQT1${cr}\ +${esc}&f2k2a16d7L Oliver QQQQT2${cr}\ +${esc}&f3k2a16d7L Dinah QQQQT3${cr}\ +${esc}&f4k2a16d7LCheshire QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllbld01 QQQQT5${cr}\ +${esc}&f6k2a16d7Lcllbld02 QQQQT6${cr}\ +${esc}&f7k2a16d7Lcllbld03 QQQQT7${cr}\ +${esc}&f8k2a16d7Lhpcll321 QQQQT8${cr}\c" + fi + + # Turn on Function keys to see changes + print "${esc}&jB\c" +else + print -u2 "Sorry but the terminal type $TERM, is not supported" + exit 1 +fi diff --git a/clients/HP/bin/fk3 b/clients/HP/bin/fk3 new file mode 100644 index 0000000..3ded632 --- /dev/null +++ b/clients/HP/bin/fk3 @@ -0,0 +1,98 @@ +#!/bin/ksh +################################################################################ +# +# File: .tep +# RCS: $Header: .tep,v 1.5 97/10/05 22:31:46 defaria Exp $ +# Description: Wrapper script to set function keys for the TEP Console +# Concentrators. +# Author: Andrew DeFaria, California Language Labs +# Created: Thu Jun 6 08:31:57 PDT 1996 +# Modified: Thu Jun 6 08:32:13 PDT 1996 (Andrew DeFaria) defaria@spock +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) +export esc=$(print "\033") +export cr=$(print "\015") + +if [ "$TERM" = "hpterm" \ + -o "$TERM" = "hp" \ + -o "$TERM" = "2394" \ + -o "$TERM" = "70096" ]; then + # Turn: + # . Enq/Ack: No + # . RecvPace: Xon/Xoff + # . InhHndShk (G): Yes + # . Inh DC2 (H): Yes + print "${esc}&q0n1h${esc}&s1g1H\c" + + if [ "$me" = fk1 ]; then + print "\ +${esc}&f1k2a16d7L Young hpcleareQQQQT1${cr}\ +${esc}&f2k2a16d7L Loomis hpclsv1 QQQQT2${cr}\ +${esc}&f3k2a16d7L hpcll237QQQQT3${cr}\ +${esc}&f4k2a16d7L Stablerhpclear1QQQQT4${cr}\ +${esc}&f5k2a16d7L Unitas hpclear3QQQQT5${cr}\ +${esc}&f6k2a16d7L Griese hpclear5QQQQT6${cr}\ +${esc}&f7k2a16d7L Simms hpclear7QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk2 ]; then + print "\ +${esc}&f1k2a16d7LDynamitehpclldynQQQQT1${cr}\ +${esc}&f2k2a16d7L Mobius hpclang6QQQQT2${cr}\ +${esc}&f3k2a16d7L Starr hpclearnQQQQT3${cr}\ +${esc}&f4k2a16d7L hpcll208QQQQT4${cr}\ +${esc}&f5k2a16d7LTarkentnhpclear9QQQQT5${cr}\ +${esc}&f6k2a16d7L Veil QQQQT6${cr}\ +${esc}&f7k2a16d7L Kilmer hpclearkQQQQT7${cr}\ +${esc}&f8k2a16d7L hpcll207QQQQT8${cr}\c" + elif [ $me = fk3 ]; then + print "\ +${esc}&f1k2a16d7LDuchess QQQQT1${cr}\ +${esc}&f2k2a16d7LCatbert QQQQT2${cr}\ +${esc}&f3k2a16d7L Nala QQQQT3${cr}\ +${esc}&f4k2a16d7LPywacket QQQQT4${cr}\ +${esc}&f5k2a16d7L Alley QQQQT5${cr}\ +${esc}&f6k2a16d7L QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk4 ]; then + print "\ +${esc}&f1k2a16d7L Wampus QQQQT1${cr}\ +${esc}&f2k2a16d7Lcllvob01 QQQQT2${cr}\ +${esc}&f3k2a16d7Lcllvob02 QQQQT3${cr}\ +${esc}&f4k2a16d7Lcllvob03 QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllvob04 QQQQT5${cr}\ +${esc}&f6k2a16d7L QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk5 ]; then + print "\ +${esc}&f1k2a16d7Lcllvob05 QQQQT1${cr}\ +${esc}&f2k2a16d7Lcllvob06 QQQQT2${cr}\ +${esc}&f3k2a16d7Lcllvob07 QQQQT3${cr}\ +${esc}&f4k2a16d7Lcllvob08 QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllvob09 QQQQT5${cr}\ +${esc}&f6k2a16d7Lcllvob10 QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk6 ]; then + print "\ +${esc}&f1k2a16d7L Gideon QQQQT1${cr}\ +${esc}&f2k2a16d7L Oliver QQQQT2${cr}\ +${esc}&f3k2a16d7L Dinah QQQQT3${cr}\ +${esc}&f4k2a16d7LCheshire QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllbld01 QQQQT5${cr}\ +${esc}&f6k2a16d7Lcllbld02 QQQQT6${cr}\ +${esc}&f7k2a16d7Lcllbld03 QQQQT7${cr}\ +${esc}&f8k2a16d7Lhpcll321 QQQQT8${cr}\c" + fi + + # Turn on Function keys to see changes + print "${esc}&jB\c" +else + print -u2 "Sorry but the terminal type $TERM, is not supported" + exit 1 +fi diff --git a/clients/HP/bin/fk4 b/clients/HP/bin/fk4 new file mode 100644 index 0000000..3ded632 --- /dev/null +++ b/clients/HP/bin/fk4 @@ -0,0 +1,98 @@ +#!/bin/ksh +################################################################################ +# +# File: .tep +# RCS: $Header: .tep,v 1.5 97/10/05 22:31:46 defaria Exp $ +# Description: Wrapper script to set function keys for the TEP Console +# Concentrators. +# Author: Andrew DeFaria, California Language Labs +# Created: Thu Jun 6 08:31:57 PDT 1996 +# Modified: Thu Jun 6 08:32:13 PDT 1996 (Andrew DeFaria) defaria@spock +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) +export esc=$(print "\033") +export cr=$(print "\015") + +if [ "$TERM" = "hpterm" \ + -o "$TERM" = "hp" \ + -o "$TERM" = "2394" \ + -o "$TERM" = "70096" ]; then + # Turn: + # . Enq/Ack: No + # . RecvPace: Xon/Xoff + # . InhHndShk (G): Yes + # . Inh DC2 (H): Yes + print "${esc}&q0n1h${esc}&s1g1H\c" + + if [ "$me" = fk1 ]; then + print "\ +${esc}&f1k2a16d7L Young hpcleareQQQQT1${cr}\ +${esc}&f2k2a16d7L Loomis hpclsv1 QQQQT2${cr}\ +${esc}&f3k2a16d7L hpcll237QQQQT3${cr}\ +${esc}&f4k2a16d7L Stablerhpclear1QQQQT4${cr}\ +${esc}&f5k2a16d7L Unitas hpclear3QQQQT5${cr}\ +${esc}&f6k2a16d7L Griese hpclear5QQQQT6${cr}\ +${esc}&f7k2a16d7L Simms hpclear7QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk2 ]; then + print "\ +${esc}&f1k2a16d7LDynamitehpclldynQQQQT1${cr}\ +${esc}&f2k2a16d7L Mobius hpclang6QQQQT2${cr}\ +${esc}&f3k2a16d7L Starr hpclearnQQQQT3${cr}\ +${esc}&f4k2a16d7L hpcll208QQQQT4${cr}\ +${esc}&f5k2a16d7LTarkentnhpclear9QQQQT5${cr}\ +${esc}&f6k2a16d7L Veil QQQQT6${cr}\ +${esc}&f7k2a16d7L Kilmer hpclearkQQQQT7${cr}\ +${esc}&f8k2a16d7L hpcll207QQQQT8${cr}\c" + elif [ $me = fk3 ]; then + print "\ +${esc}&f1k2a16d7LDuchess QQQQT1${cr}\ +${esc}&f2k2a16d7LCatbert QQQQT2${cr}\ +${esc}&f3k2a16d7L Nala QQQQT3${cr}\ +${esc}&f4k2a16d7LPywacket QQQQT4${cr}\ +${esc}&f5k2a16d7L Alley QQQQT5${cr}\ +${esc}&f6k2a16d7L QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk4 ]; then + print "\ +${esc}&f1k2a16d7L Wampus QQQQT1${cr}\ +${esc}&f2k2a16d7Lcllvob01 QQQQT2${cr}\ +${esc}&f3k2a16d7Lcllvob02 QQQQT3${cr}\ +${esc}&f4k2a16d7Lcllvob03 QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllvob04 QQQQT5${cr}\ +${esc}&f6k2a16d7L QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk5 ]; then + print "\ +${esc}&f1k2a16d7Lcllvob05 QQQQT1${cr}\ +${esc}&f2k2a16d7Lcllvob06 QQQQT2${cr}\ +${esc}&f3k2a16d7Lcllvob07 QQQQT3${cr}\ +${esc}&f4k2a16d7Lcllvob08 QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllvob09 QQQQT5${cr}\ +${esc}&f6k2a16d7Lcllvob10 QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk6 ]; then + print "\ +${esc}&f1k2a16d7L Gideon QQQQT1${cr}\ +${esc}&f2k2a16d7L Oliver QQQQT2${cr}\ +${esc}&f3k2a16d7L Dinah QQQQT3${cr}\ +${esc}&f4k2a16d7LCheshire QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllbld01 QQQQT5${cr}\ +${esc}&f6k2a16d7Lcllbld02 QQQQT6${cr}\ +${esc}&f7k2a16d7Lcllbld03 QQQQT7${cr}\ +${esc}&f8k2a16d7Lhpcll321 QQQQT8${cr}\c" + fi + + # Turn on Function keys to see changes + print "${esc}&jB\c" +else + print -u2 "Sorry but the terminal type $TERM, is not supported" + exit 1 +fi diff --git a/clients/HP/bin/fk5 b/clients/HP/bin/fk5 new file mode 100644 index 0000000..3ded632 --- /dev/null +++ b/clients/HP/bin/fk5 @@ -0,0 +1,98 @@ +#!/bin/ksh +################################################################################ +# +# File: .tep +# RCS: $Header: .tep,v 1.5 97/10/05 22:31:46 defaria Exp $ +# Description: Wrapper script to set function keys for the TEP Console +# Concentrators. +# Author: Andrew DeFaria, California Language Labs +# Created: Thu Jun 6 08:31:57 PDT 1996 +# Modified: Thu Jun 6 08:32:13 PDT 1996 (Andrew DeFaria) defaria@spock +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) +export esc=$(print "\033") +export cr=$(print "\015") + +if [ "$TERM" = "hpterm" \ + -o "$TERM" = "hp" \ + -o "$TERM" = "2394" \ + -o "$TERM" = "70096" ]; then + # Turn: + # . Enq/Ack: No + # . RecvPace: Xon/Xoff + # . InhHndShk (G): Yes + # . Inh DC2 (H): Yes + print "${esc}&q0n1h${esc}&s1g1H\c" + + if [ "$me" = fk1 ]; then + print "\ +${esc}&f1k2a16d7L Young hpcleareQQQQT1${cr}\ +${esc}&f2k2a16d7L Loomis hpclsv1 QQQQT2${cr}\ +${esc}&f3k2a16d7L hpcll237QQQQT3${cr}\ +${esc}&f4k2a16d7L Stablerhpclear1QQQQT4${cr}\ +${esc}&f5k2a16d7L Unitas hpclear3QQQQT5${cr}\ +${esc}&f6k2a16d7L Griese hpclear5QQQQT6${cr}\ +${esc}&f7k2a16d7L Simms hpclear7QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk2 ]; then + print "\ +${esc}&f1k2a16d7LDynamitehpclldynQQQQT1${cr}\ +${esc}&f2k2a16d7L Mobius hpclang6QQQQT2${cr}\ +${esc}&f3k2a16d7L Starr hpclearnQQQQT3${cr}\ +${esc}&f4k2a16d7L hpcll208QQQQT4${cr}\ +${esc}&f5k2a16d7LTarkentnhpclear9QQQQT5${cr}\ +${esc}&f6k2a16d7L Veil QQQQT6${cr}\ +${esc}&f7k2a16d7L Kilmer hpclearkQQQQT7${cr}\ +${esc}&f8k2a16d7L hpcll207QQQQT8${cr}\c" + elif [ $me = fk3 ]; then + print "\ +${esc}&f1k2a16d7LDuchess QQQQT1${cr}\ +${esc}&f2k2a16d7LCatbert QQQQT2${cr}\ +${esc}&f3k2a16d7L Nala QQQQT3${cr}\ +${esc}&f4k2a16d7LPywacket QQQQT4${cr}\ +${esc}&f5k2a16d7L Alley QQQQT5${cr}\ +${esc}&f6k2a16d7L QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk4 ]; then + print "\ +${esc}&f1k2a16d7L Wampus QQQQT1${cr}\ +${esc}&f2k2a16d7Lcllvob01 QQQQT2${cr}\ +${esc}&f3k2a16d7Lcllvob02 QQQQT3${cr}\ +${esc}&f4k2a16d7Lcllvob03 QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllvob04 QQQQT5${cr}\ +${esc}&f6k2a16d7L QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk5 ]; then + print "\ +${esc}&f1k2a16d7Lcllvob05 QQQQT1${cr}\ +${esc}&f2k2a16d7Lcllvob06 QQQQT2${cr}\ +${esc}&f3k2a16d7Lcllvob07 QQQQT3${cr}\ +${esc}&f4k2a16d7Lcllvob08 QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllvob09 QQQQT5${cr}\ +${esc}&f6k2a16d7Lcllvob10 QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk6 ]; then + print "\ +${esc}&f1k2a16d7L Gideon QQQQT1${cr}\ +${esc}&f2k2a16d7L Oliver QQQQT2${cr}\ +${esc}&f3k2a16d7L Dinah QQQQT3${cr}\ +${esc}&f4k2a16d7LCheshire QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllbld01 QQQQT5${cr}\ +${esc}&f6k2a16d7Lcllbld02 QQQQT6${cr}\ +${esc}&f7k2a16d7Lcllbld03 QQQQT7${cr}\ +${esc}&f8k2a16d7Lhpcll321 QQQQT8${cr}\c" + fi + + # Turn on Function keys to see changes + print "${esc}&jB\c" +else + print -u2 "Sorry but the terminal type $TERM, is not supported" + exit 1 +fi diff --git a/clients/HP/bin/fk6 b/clients/HP/bin/fk6 new file mode 100644 index 0000000..3ded632 --- /dev/null +++ b/clients/HP/bin/fk6 @@ -0,0 +1,98 @@ +#!/bin/ksh +################################################################################ +# +# File: .tep +# RCS: $Header: .tep,v 1.5 97/10/05 22:31:46 defaria Exp $ +# Description: Wrapper script to set function keys for the TEP Console +# Concentrators. +# Author: Andrew DeFaria, California Language Labs +# Created: Thu Jun 6 08:31:57 PDT 1996 +# Modified: Thu Jun 6 08:32:13 PDT 1996 (Andrew DeFaria) defaria@spock +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) +export esc=$(print "\033") +export cr=$(print "\015") + +if [ "$TERM" = "hpterm" \ + -o "$TERM" = "hp" \ + -o "$TERM" = "2394" \ + -o "$TERM" = "70096" ]; then + # Turn: + # . Enq/Ack: No + # . RecvPace: Xon/Xoff + # . InhHndShk (G): Yes + # . Inh DC2 (H): Yes + print "${esc}&q0n1h${esc}&s1g1H\c" + + if [ "$me" = fk1 ]; then + print "\ +${esc}&f1k2a16d7L Young hpcleareQQQQT1${cr}\ +${esc}&f2k2a16d7L Loomis hpclsv1 QQQQT2${cr}\ +${esc}&f3k2a16d7L hpcll237QQQQT3${cr}\ +${esc}&f4k2a16d7L Stablerhpclear1QQQQT4${cr}\ +${esc}&f5k2a16d7L Unitas hpclear3QQQQT5${cr}\ +${esc}&f6k2a16d7L Griese hpclear5QQQQT6${cr}\ +${esc}&f7k2a16d7L Simms hpclear7QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk2 ]; then + print "\ +${esc}&f1k2a16d7LDynamitehpclldynQQQQT1${cr}\ +${esc}&f2k2a16d7L Mobius hpclang6QQQQT2${cr}\ +${esc}&f3k2a16d7L Starr hpclearnQQQQT3${cr}\ +${esc}&f4k2a16d7L hpcll208QQQQT4${cr}\ +${esc}&f5k2a16d7LTarkentnhpclear9QQQQT5${cr}\ +${esc}&f6k2a16d7L Veil QQQQT6${cr}\ +${esc}&f7k2a16d7L Kilmer hpclearkQQQQT7${cr}\ +${esc}&f8k2a16d7L hpcll207QQQQT8${cr}\c" + elif [ $me = fk3 ]; then + print "\ +${esc}&f1k2a16d7LDuchess QQQQT1${cr}\ +${esc}&f2k2a16d7LCatbert QQQQT2${cr}\ +${esc}&f3k2a16d7L Nala QQQQT3${cr}\ +${esc}&f4k2a16d7LPywacket QQQQT4${cr}\ +${esc}&f5k2a16d7L Alley QQQQT5${cr}\ +${esc}&f6k2a16d7L QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk4 ]; then + print "\ +${esc}&f1k2a16d7L Wampus QQQQT1${cr}\ +${esc}&f2k2a16d7Lcllvob01 QQQQT2${cr}\ +${esc}&f3k2a16d7Lcllvob02 QQQQT3${cr}\ +${esc}&f4k2a16d7Lcllvob03 QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllvob04 QQQQT5${cr}\ +${esc}&f6k2a16d7L QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk5 ]; then + print "\ +${esc}&f1k2a16d7Lcllvob05 QQQQT1${cr}\ +${esc}&f2k2a16d7Lcllvob06 QQQQT2${cr}\ +${esc}&f3k2a16d7Lcllvob07 QQQQT3${cr}\ +${esc}&f4k2a16d7Lcllvob08 QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllvob09 QQQQT5${cr}\ +${esc}&f6k2a16d7Lcllvob10 QQQQT6${cr}\ +${esc}&f7k2a16d7L QQQQT7${cr}\ +${esc}&f8k2a16d5L Dis connect QQQQ${cr}\c" + elif [ $me = fk6 ]; then + print "\ +${esc}&f1k2a16d7L Gideon QQQQT1${cr}\ +${esc}&f2k2a16d7L Oliver QQQQT2${cr}\ +${esc}&f3k2a16d7L Dinah QQQQT3${cr}\ +${esc}&f4k2a16d7LCheshire QQQQT4${cr}\ +${esc}&f5k2a16d7Lcllbld01 QQQQT5${cr}\ +${esc}&f6k2a16d7Lcllbld02 QQQQT6${cr}\ +${esc}&f7k2a16d7Lcllbld03 QQQQT7${cr}\ +${esc}&f8k2a16d7Lhpcll321 QQQQT8${cr}\c" + fi + + # Turn on Function keys to see changes + print "${esc}&jB\c" +else + print -u2 "Sorry but the terminal type $TERM, is not supported" + exit 1 +fi diff --git a/clients/HP/bin/get_info b/clients/HP/bin/get_info new file mode 100644 index 0000000..db6dd58 --- /dev/null +++ b/clients/HP/bin/get_info @@ -0,0 +1,257 @@ +#! /bin/ksh +# +# Usage get_info { all_projects | +# all_members | +# all_project_members | +# member_project { asok | dgross } | +# project_attribute { Codegen vwstation} | +# member_attribute { asok phone } | +# server_names { +# buildserver | +# mailserver | +# viewserver | +# virtualws | +# vobserver | +# webserver } | +# is_project +# emit_error { error_code returned by get_info() +} +# } +# +# +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Set machines +machines=${machines:-$adm_base/data/machines} + +# Source functions +. $adm_fpath/common + +DBDIR=$adm_base/data/db/get_info +alias grep='grep -i' + +# +trap "cleanup" 1 2 3 8 15 + +####### Begin error message catalog. ############# +typeset MSG +MSG[0]="get_info:LDAP CONNECTION SUCCESSFUL" +MSG[1]="get_info:LDAP OPERATIONS ERROR" +MSG[2]="get_info:LDAP PROTOCOL ERROR" +MSG[3]="get_info:LDAP TIMELIMIT EXCEEDED" +MSG[4]="get_info:LDAP SIZELIMIT EXCEEDED" +MSG[5]="get_info:LDAP COMPARE FALSE" +MSG[6]="get_info:LDAP COMPARE TRUE" +MSG[7]="get_info:LDAP STRONG AUTH NOT SUPPORTED" +MSG[8]="get_info:LDAP STRONG AUTH REQUIRED" +MSG[9]="get_info:LDAP PARTIAL RESULTS" + +MSG[16]="get_info:LDAP NO SUCH ATTRIBUTE" +MSG[17]="get_info:LDAP UNDEFINED TYPE" +MSG[18]="get_info:LDAP INAPPROPRIATE MATCHING " +MSG[19]="get_info:LDAP CONSTRAINT VIOLATION " +MSG[20]="get_info:LDAP TYPE OR VALUE EXISTS " +MSG[21]="get_info:LDAP INVALID SYNTAX " + +MSG[32]="get_info:LDAP NO SUCH OBJECT " +MSG[33]="get_info:LDAP ALIAS PROBLEM " +MSG[34]="get_info:LDAP INVALID DN SYNTAX " +MSG[35]="get_info:LDAP IS LEAF " +MSG[36]="get_info:LDAP ALIAS DEREF PROBLEM " +MSG[37]="NAME ERROR(n) ((n & 0xf0) == 0x20)" + +MSG[48]="get_info:LDAP INAPPROPRIATE AUTH " +MSG[49]="get_info:LDAP INVALID CREDENTIALS " +MSG[50]="get_info:LDAP INSUFFICIENT ACCESS " +MSG[51]="get_info:LDAP BUSY " +MSG[52]="get_info:LDAP UNAVAILABLE " +MSG[53]="get_info:LDAP UNWILLING TO PERFORM " +MSG[54]="get_info:LDAP LOOP DETECT " + +MSG[64]="get_info:LDAP NAMING VIOLATION " +MSG[65]="get_info:LDAP OBJECT CLASS VIOLATION " +MSG[66]="get_info:LDAP NOT ALLOWED ON NONLEAF " +MSG[67]="get_info:LDAP NOT ALLOWED ON RDN " +MSG[68]="get_info:LDAP ALREADY EXISTS " +MSG[69]="get_info:LDAP NO OBJECT CLASS MODS " +MSG[70]="get_info:LDAP RESULTS TOO LARGE " + +MSG[80]="get_info:LDAP OTHER " +MSG[81]="get_info:LDAP SERVER DOWN " +MSG[82]="get_info:LDAP LOCAL ERROR " +MSG[83]="get_info:LDAP ENCODING ERROR " +MSG[84]="get_info:LDAP DECODING ERROR " +MSG[85]="get_info:LDAP TIMEOUT " +MSG[86]="get_info:LDAP AUTH UNKNOWN " +MSG[87]="get_info:LDAP FILTER ERROR " +MSG[88]="get_info:LDAP USER CANCELLED " +MSG[89]="get_info:LDAP PARAM ERROR " +MSG[90]="get_info:LDAP NO MEMORY " +MSG[91]="get_info:LDAP CONNECT ERROR " +# +# errors related to remsh or OS interface. +MSG[201]="get_info:ERROR while fixing .rhosts file in preparation for +remsh." +MSG[202]="get_info:ERROR remsh failed while invoking get_info on CLLWEB" +MSG[203]="get_info:ERROR uname returned un-supported OS." +# +# User interface errors while using get_info() +MSG[210]="get_info:ERROR insufficient arguments to get_info()" +MSG[211]="get_info:ERROR requested member project not found in data-base." +MSG[212]="get_info:ERROR requested project not found in data-base." +MSG[213]="get_info:ERROR database directory not found." +MSG[214]="get_info:ERROR gven member not found in data-base." +# +# +MSG[500]="get_info:ERROR BAD/un-implemented Error number" +####### End error message catalog. ############# + +print_error_msg() +{ +[ -z "${MSG[$1]}" ] && echo ${MSG[500]} || echo ${MSG[$1]} +exit 0 +} + +function cleanup +{ +exit $1 +} + +####################################################### +# Start processing questions . . . +# Get member project name for a given member name. +# usage# save_get_info member_project amitp +# +# This function will save information from data-base in +# a temporary file, so that cleanup can prefix this +# output with error code. This is done to pass error +# code returned by the command passed to remsh. +# +# Ex: remsh "get_info member_project amitp" +# +# remsh returns status of itself(remsh) not the status +# of "get_info". +# +save_get_info() +{ +case "$1" in +all_project_members) + # Get all members for a given project. + [ $# -ne 2 ] && cleanup 210 + [ ! -f $2.proj ] && cleanup 212 + RESULT=`grep "^project:$2" *.mem | awk -F. '{print $1}'` + ;; + +member_project) + # Get member project name for a given member name. + [ $# -ne 2 ] && cleanup 210 + [ ! -f $2.mem ] && cleanup 214 + RESULT=`grep "^project:" $2.mem | awk -F: '{print $2}'` + ;; + +project_attribute) + # Get project attribute for a given project name. + [ ! -f $2.proj ] && cleanup 212 + [ $# -eq 3 ] && RESULT=`grep "^$3:" $2.proj | awk -F: '{print $2}'` +|| RESULT=`awk -F: '{printf "%s= %s\n", $1, $2}' $2.proj` + ;; + +member_attribute) + # Get member attribute for a given member name. + [ ! -f $2.mem ] && cleanup 212 + [ $# -eq 3 ] && RESULT=`grep "^$3:" $2.mem | awk -F: '{print $2}'` +|| RESULT=`awk -F: '{printf "%s = %s\n", $1, $2}' $2.mem` + ;; + +machine_attribute) + # Get machine attribute for a given machine name. + [ ! -f $2.mach ] && cleanup 212 + [ $# -eq 3 ] && RESULT=`grep "^$3:" $2.mach | awk -F: '{print $2}'` +|| RESULT=`awk -F: '{printf "%s = %s\n", $1, $2}' $2.mach` + ;; + +is_project) + # Get project attribute for a given project name. + [ $# -ne 2 ] && cleanup 210 + [ ! -f $2.proj ] && cleanup 212 + RESULT=`grep "^project:" $2.proj | awk -d: '{print $2}'` + ;; + +all_projects) + RESULT=`/bin/ls *.proj | awk -F. '{print $1}'` + ;; + +all_members) + RESULT=`/bin/ls *.mem | awk -F. '{print $1}'` + ;; + +server_names) + [ $# -ne 2 ] && cleanup 210 + case $2 in + buildserver) + grepfor="Build Server" + ;; + mailserver) + grepfor="Mail Server" + ;; + viewserver) + grepfor="View Server" + ;; + virtualws) + grepfor="Virtual Workstation" + ;; + vobserver) + grepfor="Vob Server" + ;; + webserver) + grepfor="Web Server" + ;; + *) + cleanup 210 + ;; + esac +# RESULT=`grep "^server:$2" *.mach | awk -F. '{print $1}'` + RESULT=`grep -v "^#" $machines | grep ":Infrastructure:" | grep +"$grepfor" | awk -F: '{print $1}'` + ;; +*) + # Error in input argument. + cleanup 210 + ;; +esac +} + +######################## Main starts here ############ +# + +case $1 in +-qldap) + shift + typeset integer MULTILINE=1 + ;; +noloop) + shift + # old version compatibility + ;; + +emit_error) + [ $# -eq 2 ] && print_error_msg $2 || exit 210 # Incorrect argument +list. + ;; +*) + # go to processing outside case block + ;; +esac + +[ ! -d $DBDIR ] && cleanup 213 +cd $DBDIR +save_get_info $@ +[ $MULTILINE ] && echo "$RESULT" || echo $RESULT +cleanup 0 diff --git a/clients/HP/bin/infrastructuremach b/clients/HP/bin/infrastructuremach new file mode 100644 index 0000000..64dd505 --- /dev/null +++ b/clients/HP/bin/infrastructuremach @@ -0,0 +1,60 @@ +#!/bin/ksh +################################################################################ +# +# File: infrastructuremach +# Description: A script to execute a command on all infrastructure class machines +# Author: Andrew@DeFaria.com +# Created: Thu May 11 11:08:24 PDT 2000 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +# Set machines +machines=${machines:-$adm_base/data/machines} + +if [ "$1" = "-f" ]; then + shift + machines="$1" + shift +fi + +PATH=/adm/bin:$PATH + +if [ "$1" = "-r" ]; then + root=yes + shift +fi + +for infrastructure_machine in $(grep -ve ^# $machines | grep Infrastructure +| cut -d: -f1); do + # Execute command. Note if no command is given then the effect is to + # rlogin to each machine. + print "$infrastructure_machine:$@" + if [ $# -gt 0 ]; then + if [ -z "$root" ]; then + remsh $infrastructure_machine -n "$@" + else + root remsh $infrastructure_machine -n "$@" + fi + else + if [ -z "$root" ]; then + remsh $infrastructure_machine + else + root remsh $infrastructure_machine + fi + fi +done diff --git a/clients/HP/bin/insert b/clients/HP/bin/insert new file mode 100644 index 0000000..29fbda9 --- /dev/null +++ b/clients/HP/bin/insert @@ -0,0 +1,522 @@ +#!/usr/bin/ksh + +# Logfile +logfile=/new.system.1.log + +## Set global env variables +# Set me +me=${0##*/} + +# Set OS +OS=$(uname -r | cut -c3-) + +# Get configfiles from Bismol (IP address is used because the machine is not +# currently setup enough to know how to resolve bismol to an IP address) +configfiles_machine=15.0.96.154 + +# Set step_nbr +integer step_nbr=0 + +# Filename for configuration files +configfiles=${OS}configfiles.shar + +function error { + print -u2 "$me: Error: $1" +} # error + +function warning { + print -u2 "$me: Warning: $1" +} # warning + +function display { + print "$1" +} # display + +function info { + display "$me: Info: $1" +} # info + +function verbose { + if [ ! -z "$verbose" ]; then + display "$1" + fi +} # verbose + +function debug { + if [ ! -z "$debug" ]; then + print -u2 "$me: Debug: $1" + fi +} # debug + +function usage { + display "$ME -c/learcase [-v|verbose] [-d|debug] [-usage]" + display " -c/learcase Perform ClearCase installation" + display " -v|verbose: Turns on verbose mode" + display " -d|debug: Turns on debug mode" + display " -usage: Print this usage message" + display " " + display "The following options will be prompted for if not supplied on the" + display "command line. If any parameter has spaces in it then you need to" + display "surround it in quotes (e.g. -owners_fullname \"Andrew DeFaria\"." + display "You'll probably need to do this for the first 3 in the list below:" + display " " + display " -owners_fullname Specify owners full name" + display " -machine_usage Specify what this machine is to be used for" + display " -location Specify where this machine is located" + display " -owners_email Specify email address (no @cup.hp.com)" + display " -owners_extension Specify phone extenstion in the format of" + display " 7-XXXX (the t-44 will be prepended)" + display " -new_machine_name Specify the name of this system (REQUIRED)" + + error "$1" + exit 1 +} # usage + +function step { + let step_nbr=step_nbr+1 + display "Step #$step_nbr: $@" +} # step + +function get_configfiles { + user=anonymous + passwd=$LOGNAME@$(uname -n).cup.hp.com + directory=pub/Configuration + cd / + ftp -n $configfiles_machine <<@EOD +user $user $passwd +cd $directory +get $configfiles +quit +@EOD + + return $? +} # get_configfiles + +function export_disks { + # First check to see if all local disks are exported + cut -f1 -d' ' /etc/xtab > /tmp/xtab + bdf -t hfs | grep "/dev" | grep -v "/stand" | awk '{print $NF}' > +/tmp/exports + + if $(diff /tmp/exports /tmp/xtab > /dev/null 2>&1); then + verbose "All local disks exported" + else + verbose "Some local disks are not exported" + if [ "$mode" != "check" ]; then + verbose "Fixing the problem..." + cp /etc/exports /etc/exports.old + cp /tmp/exports /etc/exports + verbose "Exporting all disks..." + /usr/sbin/exportfs -a + verbose "Done" + fi + fi +} # export_disks + +function display_options { + display "Setup this machine according to the following profile:" + print - +-------------------------------------------------------------------------------- + + display "Clearcase:\t\t\c" + if [ "_$clearcase" = "_" ]; then + display "No" + else + display "Yes" + fi + + display "Verbose Mode:\t\t\c" + if [ "_$verbose" = "_" ]; then + display "Off" + else + display "On" + fi + + display "Debug Mode:\t\t\c" + if [ "_$debug" = "_" ]; then + display "Off" + else + display "On" + fi + + display "Machine Name:\t\t$new_machine_name" + display "Machine Usage:\t\t$machine_usage" + display "Macine Location:\t$location" + display "Owner's Fullname:\t$owners_fullname" + display "Owner's Email:\t\t$owners_email" + display "Owner's Extension:\t$owners_extension" +} # display_options + +# Set initial parm values +display +display "\t\tWelcome to the new system setup script" +display "\t\tThis is the first script of 4 that you" +display "\t\twill need to run to setup a new Virtual" +display "\t\tWorkstation Server or Buildpool Server." +display +clearcase= +verbose= +debug= +owners_fullname= +owners_email= +owners_extension= +machine_usage= +location= +new_machine_name= + +# Get parameters +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -v|-verbose) + verbose=yes + ;; + + -d|-debug) + debug=yes + ;; + + -c|-clearcase) + clearcase=y + ;; + + -owners_fullname) + if [ $# -le 1 ]; then + usage "Owner's Full Name is not specified!" + fi + shift + owners_fullname="$1" + ;; + + -machine_usage) + if [ $# -le 1 ]; then + usage "Machine Usage was not specified!" + fi + shift + machine_usage="$1" + ;; + + -location) + if [ $# -le 1 ]; then + usage "Location was not specified!" + fi + shift + location="$1" + ;; + + -owners_email) + if [ $# -le 1 ]; then + usage "Owner's Email was not specified!" + fi + shift + owners_email="$1" + ;; + + -owners_extension) + if [ $# -le 1 ]; then + usage "Owner's Extention was not specified!" + fi + shift + owners_extension="$1" + ;; + + -new_machine_name) + if [ $# -le 1 ]; then + usage "New Machine Name not specified!" + fi + shift + new_machine_name="$1" + ;; + + *) + usage "Unrecognized parameter $1" + ;; + esac + shift +done + +# Prompt for options not specified on the command line + +if [ "_$clearcase" = "_" ]; then + print "Do you wish to install Clearcase?" + print "[y/n]> \c" + read clearcase + if [ "_$clearcase" = "_" ]; then + error "You must specify y or n" + exit 1 fi +fi + +if [ "_$owners_fullname" = "_" ]; then + print "Owner's Fullname" + print "> \c" + read owners_fullname + if [ "_$owners_fullname" = "_" ]; then + owners_fullname=Unknown + fi +fi + +if [ "_$machine_usage" = "_" ]; then + print "What is this machine used for?" + print "> \c" + read machine_usage + if [ "_$machine_usage" = "_" ]; then + machine_usage="This machine is used by \ for \" + fi +fi + +if [ "_$location" = "_" ]; then + print "Where is this machine located?" + print "> \c" + read location + if [ "_$location" = "_" ]; then + location="\" + fi +fi + +if [ "_$owners_email" = "_" ]; then + print "Owner's Email address:" + print "(Should be the same as username. This script will supply the cup.hp.com)" + print "> \c" + read owners_email + if [ "_$owners_email" = "_" ]; then + owners_email=Unknown + fi +fi + +if [ "_$owners_extension" = "_" ]; then + print "Owner's Phone extention:" + print "(Should be of the format 7-XXXX This script will prepend \"t-44\" to" + print "the entered extension)" + print "> \c" + read owners_extension + if [ "_$owners_extension" = "_" ]; then + owners_extension=7-XXXX + fi +fi + +until [ "_$new_machine_name" != "_" ]; do + new_machine_name="garbage" + print "New machine name:" + print "> \c" + read new_machine_name + + if [ "_$new_machine_name" = "_" ]; then + error "Must enter a new machine name" + fi +done + +if [ $(id -u) -ne 0 ]; then + error "Must be root to execute this command" + exit 1 +fi + +display_options + +display +display "Continue Installation (Y/n)?\c" +answer=y +read answer +case "$answer" in + y|Y|yes|Yes|YES|"") + continue + ;; + *) + display "Installation aborted. Rerun $me if you wish to install again" + exit 1 + ;; +esac + +function do_installation { +display_options + +step "Get configuration files" + +get_configfiles + +if [ $? -ne 0 ]; then + error "Unable to ftp $configfiles from $configfiles_machine" + exit 1 +fi + +step "Unpack configuration files" + +cd / +sh $configfiles >> $logfile 2>&1 +rm -f $configfiles + +step "Change GenericSysName in /etc/issue" + +sed "s/GenericSysName/$new_machine_name/" /etc/issue > /etc/issue.new +mv /etc/issue /etc/issue.old +mv /etc/issue.new /etc/issue + +step "Allow Access to at(1)" + +touch /var/adm/cron/at.deny +rm -f /var/adm/cron/at.allow + +step "Setup ClearCase Build Hosts File" +echo `uname -n` > /.bldhost.hppa +cat /etc/bldhost.hppa >> /.bldhost.hppa +rm /etc/bldhost.hppa + +step "Symlink /nfs -> /net" + +ln -s /net /nfs 2>> $logfile + +step "Symlink /usr/preserve -> /var/preserve" + +ln -s /var/preserve /usr/preserve 2>> $logfile + +step "Setup Application Server" + +/net/bismol/app/admin/bin/setup + +step "Setup Mother of All Passwords (AKA MoA)" + +/net/bismol/app/admin/bin/mkpass -f + +step "Create /etc/motd" + +banner $new_machine_name > /etc/motd +uname -a >> /etc/motd +cat >> /etc/motd <<:END + +******************************************************************************* +* This is a private system operated for the Hewlett-Packard Company business. * +* Authorization from HP management is required to use this system. * +* Use by unauthorized persons is prohibited. * +******************************************************************************* +For System Support: Mon-Fri 8:00-5:00 Email (site-ux@cup.hp.com) +Phone: t-447-1212 After hours/weekend Pre-arrange: t-447-0629 +------------------------------------------------------------------------------- +Usage: $machine_usage +Owner: $owners_fullname ($owners_email@cup.hp.com) Phone: +t-44$owners_extension +Location: $location +------------------------------------------------------------------------------- +:END + +step "Edit /etc/gettydefs: Change \"Console login:\" to \"$new_machine_name login:\"" + +sed "s/Console Login:/$new_machine_name Login:/" /etc/gettydefs \ + > /etc/gettydefs.new +mv /etc/gettydefs /etc/gettydefs.old +mv /etc/gettydefs.new /etc/gettydefs + +step "Ninstalling lp, adm and net3 packages" + +/usr/local/bin/ninstall -h bismol lp adm net3 >> $logfile 2>&1 + +step "Run netdaemon.dy" + +/usr/adm/netdist/netdaemon.dy 2>> $logfile + +step "Fix /usr/sbin/rlp" + +chmod +x /usr/sbin/rlp + +step "Install root crontab" + +crontab /crontab.root >> $logfile 2>&1 +rm -f /crontab.root + +step "Allow usage of crontab for ordinary users" + +touch /var/adm/cron/cron.deny +rm -f /var/adm/cron/cron.allow + +if [ "$clearcase" = "y" ]; then + step "Make symlink for the Build Environment" + + ln -s /CLO/BUILD_ENV/usr/lib /usr/shlib 2>> $logfile + + step "Symlinking clearmake for parallel build support" + + ln -s /usr/eclipse/bin/clearmake /usr/contrib/bin/clearmake +fi + +step "Adjust nfsd/biod's" + +integer nfsd=4 +integer biod=4 +case $(uname -m) in + 9000/712|9000/715) + ;; + + 9000/755) + nfsd=24 + biod=8 + ;; + + 9000/780|9000/813|9000/829|9000/849|9000/889|9000/898) + nfsd=48 + biod=16 + ;; + + *) + warning "Unknown machine model $(uname -m)!" + warning "Leaving nfsd/biod's as default" + ;; +esac + +if [ $nfsd -ne 4 ]; then + cp /etc/rc.config.d/nfsconf /etc/rc.config.d/nfsconf.old + sed "s/NUM_NFSD=4/NUM_NFSD=$nfsd/" /etc/rc.config.d/nfsconf \ + > /etc/rc.config.d/nfsconf.new + mv /etc/rc.config.d/nfsconf.new /etc/rc.config.d/nfsconf + sed "s/NUM_NFSIOD=4/NUM_NFSIOD=$biod/" /etc/rc.config.d/nfsconf \ + > /etc/rc.config.d/nfsconf.new + mv /etc/rc.config.d/nfsconf.new /etc/rc.config.d/nfsconf +fi + +step "Setting up for 9.x build environment" + +mv /usr/lib/libisamstub.1 /usr/lib/libisamstub.0 +cp /net/bismol/app/admin/lib/libisamstub.1 /usr/lib/libisamstub.1 +chmod 555 /usr/lib/libisamstub.1 +chown bin:bin /usr/lib/libisamstub.1 + +step "Setup DTS" + +ln -s /net/bismol/aspirin/DTS /usr/DTS 2>> $logfile + +step "Setup automounter to use hard mounts" + +echo "/net -hosts -intr" > /etc/auto_master + +step "Link /var/mail" + +mv /var/mail /var/mail.orig +ln -s /net/cllmail/var/mail/ /var/mail + +step "Fix Root's name entry in /etc/passwd.loc" + +sed "s/Root user/Root\@$(uname -n)/" /etc/passwd.loc > /tmp/passwd.loc +mv /tmp/passwd.loc /etc/passwd.loc + +step "Fix permissions on /dev/lan*" + +chmod 644 /dev/lan* + +step "Installing OptionalSoftware" + +display +display "This step will take several minutes and then the machine will +reboot." +display "After the machine is back up continue with new.system.2." + +/usr/sbin/swinstall + -s wampus:/Depots/$OS \ + -x autoreboot=true \ + OptionalSoftware \ +>> $logfile 2>&1 + +info "Swinstall complete, system will reboot if there were no errors" + +} # do_installation + +do_installation | tee $logfile diff --git a/clients/HP/bin/install_new_kernel b/clients/HP/bin/install_new_kernel new file mode 100644 index 0000000..ec18921 --- /dev/null +++ b/clients/HP/bin/install_new_kernel @@ -0,0 +1,65 @@ +#!/bin/ksh +# +# Move a new kernel, previously generated, into place +# +me=$(basename $0) + +if [ $(id -u) -ne 0 ]; then + print -u2 "$me: Error: Must be root to execute this command!" + exit 1 +fi + +OS=$(/bin/uname -r | /usr/bin/cut -f2 -d.) + +kernel_1_source= +kernel_2_source= +kernel_1_destination= +kernel_2_destination= + +case "$OS" in + 10) + kernel_1_source=/stand/build/vmunix_test + kernel_2_source=/stand/build/system.SAM + kernel_1_destination=/stand/vmunix + kernel_2_destination=/stand/system + ;; + 09) + kernel_1_source=/etc/conf/hp-ux + kernel_2_source=/etc/conf/dfile.SAM + kernel_1_destination=/hp-ux + kernel_2_destination=/etc/conf/dfile + ;; + *) + print -u2 "$me: Error: Unable to determine OS level: $OS" + exit 1 + ;; +esac + +if [ -f $kernel_1_source -a -f $kernel_2_source ]; then + answer=y + print "New kernel found, move into place (Y/n)?\c" + read answer + if [ "$answer" = "y" -o "$answer" = "Y" ]; then + print -u2 "Moving kernel into place..." + mv $kernel_1_source $kernel_1_destination + mv $kernel_2_source $kernel_2_destination + else + print "WARNING: kernel not moved into place!" + fi + + answer=y + print "Restart system (Y/n)?\c" + read answer + if [ "$answer" = "y" -o "$answer" = "Y" ]; then + cd / + /etc/shutdown -yr 0 + else + print "System not restarted" + exit + fi +else + print -u2 "$me: Error: Unable to find new kernel files:" + print -u2 "\t$kernel_1_source" + print -u2 "\t$kernel_2_source" + exit 1 +fi diff --git a/clients/HP/bin/install_pwplus b/clients/HP/bin/install_pwplus new file mode 100644 index 0000000..3004f58 --- /dev/null +++ b/clients/HP/bin/install_pwplus @@ -0,0 +1,28 @@ +#!/bin/ksh +################################################################################ +# +# File: install_pwplus +# Description: This script will install pwplus +# Author: Andrew DeFaria (defaria@cup.hp.com) +# Language: Korn Shell +# Modified: +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# First source /app/appserver +if [ -x /app/appserver ]; then + . /app/appserver +fi + +if [ $(id -u) -ne 0 ]; then + print -u2 "Error: You must be root to execute this command!" + exit 1 +fi + +if [ "$OS" = "09" ]; then + print -u2 "Error: $(basename $0) does not run on 9.x!" + exit 1 +fi + +/usr/sbin/swinstall -s medusa.corp:/var/depot/security PWplus diff --git a/clients/HP/bin/inventory b/clients/HP/bin/inventory new file mode 100644 index 0000000..c8123d2 --- /dev/null +++ b/clients/HP/bin/inventory @@ -0,0 +1,310 @@ +#!/bin/ksh +################################################################################ +# +# File: inventory +# Description: Displays information about a machine including hardware and +# software inventories +# Author: Andrew@DeFaria.com +# Created: Fri Jun 15 15:26:27 PDT 2001 +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew DeFaria, all rights reserved. +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +tmpprefix=${TMPDIR:-/tmp}/$me.$$ +tmpfile=$tmpprefix +tmpfile=~v801310/adm/data/$(hostname).sysinfo +. $adm_fpath/tmpfiles + +function usage { + print -u2 "Usage: $me -[h|ardware] -[s|oftware] -[da|emon]" + exit 1 +} # usage + +function display_inventory { + # This function displays the "inventory" for this machine. Currently + # "inventory" consists of hardware, software and daemons. + display_hardware_inventory + display_software_inventory + display_daemon_inventory +} # display_inventory + +function display_hardware_inventory { + # This function displays the hardware inventory. Only "important" hardware + # values are displayed. + # + # This function may be enhanced in the future. + debug "ENTER: $0" + + # Hostname + host=$(grep ^HOSTNAME: $tmpfile | cut -c16-) + + # OS + os=$(uname -r) + + # Model + model=$(grep ^MODEL: $tmpfile | cut -c16-) + + # CPU Speed + cpu_speed=$(grep "^CPU SPEED:" $tmpfile | cut -c16-) + + # CPUs + cpus=$(grep ^CPUS: $tmpfile | awk '{print $2}') + + # Main Memory + memory=$(grep MEMORY: $tmpfile | awk '{printf "%s %s", $2, $3}') + + # Swap + if is_root; then + swap=$(swapinfo -m -t | tail -1 | awk '{print $2}') + swap="$swap Meg" + else + swap="(Not run as root)" + fi + + # Number of disks + if is_root; then + verbose "Determining the number of disks" + nbr_disks=$(ioscan -C disk | grep -ve "^H/W" -e "^==" -e "CD-ROM" | wc -l) + else + nbr_disks="(Not run as root)" + fi + + # Volume groups and logical volumes + integer nbr_vg=0 + integer nbr_lv=0 + + verbose "Determining volume groups and logical volumes" + for vg in $(vgdisplay | grep "^VG Name" | awk '{print $NF}'); do + let nbr_vg=nbr_vg+1 + for v in $(vgdisplay -v $vg | grep " LV Name" | awk '{print $NF}'); do + let nbr_lv=nbr_lv+1 + done + done + + # Display information + display "Machine: $host" + display "OS: $os" + display "Model: $model" + display "CPUs: $cpus ($cpu_speed)" + display "Memory: $memory" + display "Swap: $swap" + display "Number of disks: $nbr_disks" + display "Volume Groups: $nbr_vg" + display "Logical Volumes: $nbr_lv" + + # Print filesystems with percentage filled in descending order: + display + bdf -l | grep -ve "^Filesystem" -e "^AFS" | + awk '{printf "%s\t%s\n", $(NF-1), $NF}' | sort -nr + debug "EXIT: $0" +} # display_hardware_inventory + +function display_software_inventory { + # This function displays the software inventory. Only "important" software + # values are displayed. Currently these are: + # + # . Certain "interesting" software registered in the SD-UX product + # database + # . Certain "interesting" 3rd party software (typically not recorded + # properly in the SD-UX product database) + # + # This function may be enhanced in the future. + debug "ENTER: $0" + + verbose "Checking for certain important software (SD-UX)" + display "\nInstalled software (SD-UX):\n" + for package in \ + Apache \ + DB2V7CAE \ + DB2V7CONN \ + DB2V7SDK \ + DB2V7WGRP \ + C-ANSI-C \ + C-Plus-Plus \ + CCASE-MVFS \ + Glance \ + Ignite-UX \ + Java-PlugIn1-2 \ + Java-Runtime1-1 \ + Java2JDK_base \ + Java2RTE_base \ + gcc \ + NscapeDir40Srv \ + NscapeFastrakSrv \ + NscapeNavGold \ + NSEntrpr36Srv \ + NSNavigator40 \ + ; do + grep "$package" $tmpfile > /dev/null 2>&1 + + if [ $? -eq 0 ]; then + if [ -n "$packages" ]; then + packages="$packages, $package" + else + packages="$package" + fi + fi + done + display "\t$packages" + + display "\nNumber of patches: \c" + grep -c "^ PH" $tmpfile + + display "\nContents of /usr/local/bin:\n\t\c" + l -F /usr/local/bin + + display "\nOther software:\n" + + for other_software_dir in \ + /sybase \ + /usr/local/flexlm \ + /opt/perl \ + /opt/perl5 \ + /opt/tuxedo \ + /opt/weblogic \ + ; do + if [ -d $other_software_dir ]; then + if [ -n "$other_software_dirs" ]; then + other_software_dirs="$other_software_dirs, $other_software_dir" + else + other_software_dirs="$other_software_dir" + fi + fi + done + + display "\t$other_software_dirs" + + debug "EXIT: $0" +} # display_software_inventory + +function display_daemon_inventory { + # This function displays the daemon inventory. Only "important" daemon + # values are displayed. + # + # This function may be enhanced in the future. + debug "ENTER: $0" + + display "\nRunning daemons on $(uname -n):\n" + + # Check to see if Oracle is running + if [ $(who | cut -f1 -d' ' | sort -u | grep -c oracle) -eq 1 ]; then + daemons="Oracle" + fi + + verbose "Checking for running daemons" + # Check other running daemons + for daemon in \ + ns-httpd \ + uxwdog \ + /opt/perf/bin/midaemon \ + /usr/afs/bin/bosserver \ + /usr/afs/bin/busserver \ + /usr/afs/bin/fileserver \ + /usr/afs/bin/kasserver \ + /usr/afs/bin/ptserver \ + /usr/afs/bin/runntp \ + /usr/afs/bin/vlserver \ + /usr/afs/bin/volserver \ + /usr/excalib/efsd \ + /usr/sbin/inetd.afs \ + /usr/vice/etc/afsd\ + ; do + integer n=$(ps -ef | grep "$daemon" | grep -v "grep $daemon" | wc -l) + + if [ $n -gt 0 ]; then + if [ -n "$daemons" ]; then + daemons="$daemons, $daemon" + else + daemons="$daemon" + fi + fi + done + display "\t$daemons" + + debug "EXIT: $0" +} # display_daemon_inventory + +# Get parms +hardware=true +softare=true +daemon=true +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -v|-verbose) + verbose=yes + ;; + + -d|-debug) + debug=yes + ;; + + -h|-hardware) + hardware=true + ;; + + -s|-software) + software=true + ;; + + -da|-daemon) + daemon=true + ;; + + *) + error "Unknown parameter encounter: \"$1\"" + usage + ;; + esac + shift +done + +# Find sysinfo + +# First check to see if we can access our own sysinfo +sysinfo=/usr/contrib/bin/sysinfo + +if [ ! -x $sysinfo ]; then + # Next check to see if we can find sysinfo on PATH + sysinfo=$(whence sysinfo) + if [ "_$sysinfo" = "_" ]; then + error "Unable to find sysinfo!" 1 + else + warning "Using nonstandard sysinfo: $sysinfo. Results may vary" + fi +fi +verbose "Using sysinfo: $sysinfo" + +# Hack alert! During testing I'm letting $tmpfile hang around. Therefore if it +# exists already we'll simply use it assuming it contains valid output from +# a previous run. This speeds up things quite a bit +if [ ! -f $tmpfile ]; then + # Check to see if user is running as root + if is_root; then + error "You must run this as root" 1 + else + verbose "Gathering information...\c" + $sysinfo -a > $tmpfile + chown v801310:sfokt $tmpfile + chmod 666 $tmpfile + verbose " done" + fi +fi + +display_inventory diff --git a/clients/HP/bin/lpsetup b/clients/HP/bin/lpsetup new file mode 100644 index 0000000..3609c22 --- /dev/null +++ b/clients/HP/bin/lpsetup @@ -0,0 +1,156 @@ +#!/usr/bin/ksh +################################################################################ +# +# File: lpsetup +# Description: Script to recreate printer definitions +# Author: Andrew@DeFaria.com +# Created: Wed Sep 6 16:38:14 PDT 2000 +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +# Global variables +printer_definitions=${printer_definitions:-$adm_base/etc/printer.defs} +local_printers= + +# Commands used +accept=/usr/sbin/accept +enable=/usr/bin/enable +lpadmin=/usr/sbin/lpadmin +lpshut=/usr/sbin/lpshut +lpsched=/usr/sbin/lpsched + +function usage { + if [ "_$1" != "_" ]; then + display "$1" + display + fi + display "Usage: $me" + exit 1 +} # usage + +function remove_all_printers { + debug "ENTER: $0" + + verbose "Removing all printers" + + # First list all known printers + all_printers=$(lpstat -s | grep device | cut -f3 -d' ' | cut -f1 -d:) + + # Now determine if the printer is local. If it's local then we do not delete + # it. + for printer in $all_printers; do + if [ $(lpstat -v$printer | wc -l) -gt 1 ]; then + verbose "Removing $printer" + $lpadmin -x$printer + else + local_printers="$local_printers $printer" + verbose "$printer is local to this machine. Will not automatically remove it" + fi + done + verbose "Removed all printers" + debug "EXIT: $0" +} # remove_all_printers + +# Check for execution by root +if is_not_root; then + error "This script must be run as root" 1 +fi + +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -v|-verbose) + verbose=yes + ;; + + -d|-debug) + debug=yes + ;; + + *) + usage "Unrecognized parameter $1" + ;; + esac + shift +done + +if [ ! -r $printer_definitions ]; then + error "Unable to find printer definitions ($printer_definitions)" 2 +fi + +if [ $(uname -r) = "B.11.11" ]; then + verbose "Prereleased OS (B.11.11) detected. No processing done" + exit 0 +fi + +# You must shutdown the spooler before making any changes +$lpshut > /dev/null 2>&1 + +if [ $? -ne 0 ]; then + error "Unable to stop LP Scheduler!" 3 +fi + +# First remove all printer definitions +remove_all_printers + +# Now add them back +verbose "Adding all printers" +grep -v "^#" $printer_definitions | grep -v "^$" | while read printer server; do + is_a_local_printer=false + for local_printer in $local_printers; do + if [ "$printer" = "$local_printer" ]; then + is_a_local_printer=true + break + fi + done + + if [ "$is_a_local_printer" = "false" ]; then + verbose "Adding $printer:$server... \c" + server=$server.cup.hp.com + $lpadmin \ + -p$printer \ + -orm$server \ + -orp$printer \ + -mrmodel \ + -v/dev/null \ + -ocmrcmodel \ + -osmrsmodel + + verbose "accepting... \c" + $accept $printer > /dev/null 2>&1 + + verbose "enabling... \c" + $enable $printer > /dev/null 2>&1 + + verbose "done" + else + verbose "Skipping local printer $printer..." + fi +done + +verbose "Added all printers" + +# Start up the print spooler +verbose "Restarting the print spooler" +$lpsched -v > /dev/null 2>&1 + +if [ $? -ne 0 ]; then + error "Unable to start LP Scheduler!" 6 +fi diff --git a/clients/HP/bin/lspatches b/clients/HP/bin/lspatches new file mode 100644 index 0000000..ca8dc86 --- /dev/null +++ b/clients/HP/bin/lspatches @@ -0,0 +1,28 @@ +#!/bin/ksh +################################################################################ +# +# File: lspatches +# RCS: $Header: lspatches,v 1.1 97/04/21 14:23:58 defaria Exp $ +# Description: Lists patches for a 10.x machine +# Author: Andrew DeFaria, California Language Labs +# Created: Mon Nov 13 16:14:30 1995 +# Modified: Mon Nov 13 16:16:56 1995 (Andrew DeFaria) defaria@spock +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# First source the appserver script +if [ -x /app/appserver ]; then + . /app/appserver +fi + +if [ $# -eq 0 ]; then + print -u2 "Patches for $(uname -n):" + /usr/sbin/swlist -l product | grep PH +else + for machine in "$@"; do + print -u2 "Patches for $machine:" + remsh $machine -n /usr/sbin/swlist -l product | grep PH + done +fi diff --git a/clients/HP/bin/lsproduct b/clients/HP/bin/lsproduct new file mode 100644 index 0000000..0b33923 --- /dev/null +++ b/clients/HP/bin/lsproduct @@ -0,0 +1,25 @@ +#!/bin/ksh +################################################################################ +# +# File: lsproduct +# RCS: $Header: lsproduct,v 1.1 96/07/24 04:01:51 defaria Exp $ +# Description: Lists products for a 10.x machine +# Author: Andrew DeFaria, California Language Labs +# Created: Wed Jul 24 04:00:36 PDT 1996 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# First source the appserver script +if [ -x /app/appserver ]; then + . /app/appserver +fi + +if [ $# -eq 0 ]; then + print -u2 "All products on $(uname -n):" + /usr/sbin/swlist -l product +else + /usr/sbin/swlist -l product "$@" +fi diff --git a/clients/HP/bin/lvs b/clients/HP/bin/lvs new file mode 100644 index 0000000..ca95ad5 --- /dev/null +++ b/clients/HP/bin/lvs @@ -0,0 +1,33 @@ +#!/bin/ksh +################################################################################ +# +# File: lvs +# RCS: $Header: lvs,v 1.1 97/04/08 15:27:22 defaria Exp $ +# Description: A script to list the LVM's and their disk devices +# Author: Jeff Bralley (Contrator), California Language Labs +# Created: Tue Apr 8 14:35:00 PDT 1997 +# Modified: Tue Apr 8 15:11:13 PDT 1997 Andrew DeFaria (defaria@cup.hp.com) +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +print "Mount Point\tLogical Volume\t\tPhysical Volume" +echo '--------------- ----------------------- ---------------' + +vgdisplay | + grep '^VG Name' | + awk '{print $3}' | + while read vgName; do + for volumeName in $vgName/*; do + if [ ! -b $volumeName ]; then + continue + fi + pd=$(lvdisplay -v $volumeName | grep '^[ ]*/dev/dsk' | awk '{print $1}') + mp=$(bdf $volumeName 2> /dev/null | grep -v Filesystem | awk '{print $NF}') + if [ "$mp" = "" ]; then + mp=swap + fi + print "$mp\t\t$volumeName\t\t$pd" + done +done diff --git a/clients/HP/bin/machine_info b/clients/HP/bin/machine_info new file mode 100644 index 0000000..1b1b4ce --- /dev/null +++ b/clients/HP/bin/machine_info @@ -0,0 +1,84 @@ +#!/bin/ksh +################################################################################ +# +# File: machine_info +# Description: Displays information about a machine +# Author: Andrew@DeFaria.com +# Created: Fri Apr 30 14:13:56 PDT 1999 +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +# Set machines +machines=${machines:-$adm_base/data/machines} + +if [ ! -f $machines ]; then + print -u2 "$me: Error: Unable to find $machines file!" + exit 1 +fi + +function display_machine_info { + machine=$1 + + ISF=" " + line=$(grep "^$machine:" $machines 2> /dev/null) + + if [ "_$line" = "_" ]; then + print -u2 "No information on machine $machine" + else + machine=$(print $line | cut -f1 -d:) + ip_address=$(print $line | cut -f2 -d:) + model=$(print $line | cut -f3 -d:) + osversion=$(print $line | cut -f4 -d:) + ccversion=$(print $line | cut -f5 -d:) + owner=$(print $line | cut -f6 -d:) + phone=$(print $line | cut -f7 -d:) + usage=$(print $line | cut -f8 -d:) + class=$(print $line | cut -f9 -d:) + location=$(print $line | cut -f10 -d:) + eclipseid=$(print $line | cut -f11 -d:) + print "Machine:\t\t$machine" + print "IP Address:\t\t$ip_address" + print "Model:\t\t\t$model" + print "OS Version:\t\t$osversion" + print "ClearCase Version:\t$ccversion" + print "Owner:\t\t\t$owner" | tr -s "(" "<" | tr -s ")" ">" + print "Phone:\t\t\t$phone" + print "Usage:\t\t\t$usage" + print "Class:\t\t\t$class" + print "Location:\t\t$location" + print "Eclipse ID:\t\t$eclipseid" + fi +} # display_machine_info + +function dump_all_machines { + grep -v "^#" $machines | cut -f1 -d: | while read machine; do + print - +-------------------------------------------------------------------------------- + display_machine_info $machine + done +} # dump_all_machines +if [ $# -eq 0 ]; then + display_machine_info $(uname -n) +else + if [ "$1" = "-all" ]; then + dump_all_machines + else + for i in $@; do + display_machine_info $i + done + fi +fi diff --git a/clients/HP/bin/machine_stats b/clients/HP/bin/machine_stats new file mode 100644 index 0000000..403244a --- /dev/null +++ b/clients/HP/bin/machine_stats @@ -0,0 +1,131 @@ +#!/bin/ksh +################################################################################ +# +# File: machine_stats +# Description: Displays statistical information about all machines +# Author: Andrew@DeFaria.com +# Created: Fri Apr 30 14:13:56 PDT 1999 +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +# Set machines +machines=${machines:-$adm_base/data/machines} + +if [ ! -f $machines ]; then + print -u2 "$me: Error: Unable to find $machines file!" + exit 1 +fi + +total_machines=$(grep -cv "^#" $machines) +total_infrastructure=$(grep -v "^#" $machines | grep -c ":Infrastructure:") +total_test=$(grep -v "^#" $machines | grep -c ":Test:") +total_desktop=$(grep -v "^#" $machines | grep -c ":Desktop:") +total_unknown=$(grep -v "^#" $machines | cut -f9 -d: | grep -c "Unknown") +total_5_6=$(grep -v "^#" $machines | grep -c "5\.6\:") +total_5_6_other=$(grep -v "^#" $machines | grep -c "5\.6[^:]") +total_11_00=$(grep -v "^#" $machines | grep -c "B\.11\.00") +total_11_11=$(grep -v "^#" $machines | grep -c "B\.11\.11") +total_10_30=$(grep -v "^#" $machines | grep -c "B\.10\.30") +total_10_20=$(grep -v "^#" $machines | grep -c "B\.10\.20") +total_10_10=$(grep -v "^#" $machines | grep -c "B\.10\.10") +total_10_01=$(grep -v "^#" $machines | grep -c "B\.10\.01") +total_4_0_cc=$(grep -v "^#" $machines | grep -c "4\.0\:") +total_3_2_cc=$(grep -v "^#" $machines | grep -c "3\.2\:") +total_3_2_1_cc=$(grep -v "^#" $machines | grep -c "3\.2\.1") +total_eclipse01=$(grep -v "^#" $machines | awk -F: '{print $11}' | grep -c +"01") +total_eclipse02=$(grep -v "^#" $machines | awk -F: '{print $11}' | grep -c +"02") +total_eclipse03=$(grep -v "^#" $machines | awk -F: '{print $11}' | grep -c +"03") +total_eclipseno=$(grep -v "^#" $machines | awk -F: '{print $11}' | grep -c +"No Eclipse") +let total_cc=total_4_0_cc+total_3_2_cc+total_3_2_1_cc + +print "PDL Machines" +let +hp_machines=total_11_00+total_11_11+total_10_30+total_10_20+total_10_10+total_10_01 + +if [ $hp_machines -gt 0 ]; then + print " " + print "HP-UX Versions:" + print - --------------- + if [ $total_11_00 -gt 0 ]; then + print "Total 11.00 .................. $total_11_00" + fi + if [ $total_11_11 -gt 0 ]; then + print "Total 11.11 .................. $total_11_11" + fi + if [ $total_10_30 -gt 0 ]; then + print "Total 10.30 .................. $total_10_30" + fi + if [ $total_10_20 -gt 0 ]; then + print "Total 10.20 .................. $total_10_20" + fi + if [ $total_10_10 -gt 0 ]; then + print "Total 10.10 .................. $total_10_10" + fi + if [ $total_10_01 -gt 0 ]; then + print "Total 10.01 .................. $total_10_01" + fi +fi +let sun_machines=total_5_6+total_5_6_other +if [ $sun_machines -gt 0 ]; then + print " " + print "Sun Versions:" + print - ------------- + print "5.6 .......................... $total_5_6" + print "5.6 (other) .................. $total_5_6_other" +fi +print " " +print "Total Machines ............... $total_machines" +print " " +print "ClearCase Machines:" +print - ------------------- +if [ $total_4_0_cc -gt 0 ]; then + print "4.0 .......................... $total_4_0_cc" +fi +if [ $total_3_2_cc -gt 0 ]; then + print "3.2 .......................... $total_3_2_cc" +fi +if [ $total_3_2_1_cc -gt 0 ]; then + print "3.2.1 ........................ $total_3_2_1_cc" +fi +if [ $total_eclipse01 -gt 0 ]; then + print "Eclipse 01 ................... $total_eclipse01" +fi +if [ $total_eclipse02 -gt 0 ]; then + print "Eclipse 02 ................... $total_eclipse02" +fi +if [ $total_eclipse03 -gt 0 ]; then + print "Eclipse 03 ................... $total_eclipse03" +fi +if [ $total_eclipseno -gt 0 ]; then + print "No Eclipse ................... $total_eclipseno" +fi +print " " +print "Total ClearCase Machines ..... $total_cc" + +print +print "Machine classes:" +print - ----------------- +print "Infrastructure ............... $total_infrastructure" +print "Test ......................... $total_test" +print "Desktop ...................... $total_desktop" +if [ $total_unknown -gt 0 ]; then + print "Unknown ...................... $total_unknown" +fi diff --git a/clients/HP/bin/make_motd b/clients/HP/bin/make_motd new file mode 100644 index 0000000..b3d83cd --- /dev/null +++ b/clients/HP/bin/make_motd @@ -0,0 +1,284 @@ +#!/usr/bin/ksh + +# Logfile +logfile=make_motd.log + +## Set global env variables +# Set me +me=${0##*/} + +# Set OS +OS=$(uname -r | cut -c3-) + +unames=$(uname -s) +unamen=$(uname -n) +unamer=$(uname -r) +unamev=$(uname -v) +unamem=$(model) +unamei=$(uname -i) +unamel=$(uname -l) + +# Set step_nbr +integer step_nbr=0 + +function error { + print -u2 "$me: Error: $1" +} # error + +function warning { + print -u2 "$me: Warning: $1" +} # warning + +function display { + print "$1" +} # display + +function info { + display "$me: Info: $1" +} # info + +function verbose { + if [ ! -z "$verbose" ]; then + display "$1" + fi +} # verbose + +function debug { + if [ ! -z "$debug" ]; then + print -u2 "$me: Debug: $1" + fi +} # debug + +function usage { + display "$me [-v|verbose] [-d|debug] [-usage]" + display " -v|verbose: Turns on verbose mode" + display " -d|debug: Turns on debug mode" + display " -usage: Print this usage message" + display " " + display "The following options will be prompted for if not supplied on the" + display "command line. If any parameter has spaces in it then you need to" + display "surround it in quotes (e.g. -owners_fullname \"Andrew DeFaria\"." + display "You'll probably need to do this for the first 3 in the list below:" + display " " + display " -owners_fullname Specify owners full name" + display " -machine_usage Specify what this machine is to be used for" + display " -location Specify where this machine is located" + display " -owners_email Specify email address (no @cup.hp.com)" + display " -owners_extension Specify phone extenstion in the format of" + display " 7-XXXX (the t-44 will be prepended)" + display " -new_machine_name Specify the name of this system (REQUIRED)" + + error "$1" + exit 1 +} # usage + +function step { + let step_nbr=step_nbr+1 + display "Step #$step_nbr: $@" +} # step + +function display_options { + display "Setup this machine according to the following profile:" + print - +-------------------------------------------------------------------------------- + + display "Machine Name:\t\t$new_machine_name" + display "Machine Usage:\t\t$machine_usage" + display "Macine Location:\t$location" + display "Owner's Fullname:\t$owners_fullname" + display "Owner's Email:\t\t$owners_email" + display "Owner's Extension:\t$owners_extension" +} # display_options + +# Set initial parm values +display +display "\t\tWelcome to the motd creation script" +display +verbose= +debug= +owners_fullname= +owners_email= +owners_extension= +machine_usage= +location= +new_machine_name= + +if [ $(id -u) -ne 0 ]; then + error "Must be root to create or modify /etc/motd" + exit 1 +fi + +# Get parameters +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -v|-verbose) + verbose=yes + ;; + + -d|-debug) + debug=yes + ;; + + -owners_fullname) + if [ $# -le 1 ]; then + usage "Owner's Full Name is not specified!" + fi + shift + owners_fullname="$1" + ;; + + -machine_usage) + if [ $# -le 1 ]; then + usage "Machine Usage was not specified!" + fi + shift + machine_usage="$1" + ;; + + -location) + if [ $# -le 1 ]; then + usage "Location was not specified!" + fi + shift + location="$1" + ;; + + -owners_email) + if [ $# -le 1 ]; then + usage "Owner's Email was not specified!" + fi + shift + owners_email="$1" + ;; + + -owners_extension) + if [ $# -le 1 ]; then + usage "Owner's Extention was not specified!" + fi + shift + owners_extension="$1" + ;; + + -new_machine_name) + if [ $# -le 1 ]; then + usage "New Machine Name not specified!" + fi + shift + new_machine_name="$1" + ;; + + *) + usage "Unrecognized parameter $1" + ;; + esac + shift +done + +# Prompt for options not specified on the command line + +if [ "_$owners_fullname" = "_" ]; then + print "Owner's Fullname" + print "> \c" + read owners_fullname + if [ "_$owners_fullname" = "_" ]; then + owners_fullname=Unknown + fi +fi + +if [ "_$machine_usage" = "_" ]; then + print "What is this machine used for?" + print "> \c" + read machine_usage + if [ "_$machine_usage" = "_" ]; then + machine_usage="This machine is used by \ for \" + fi +fi + +if [ "_$location" = "_" ]; then + print "Where is this machine located?" + print "> \c" + read location + if [ "_$location" = "_" ]; then + location="\" + fi +fi + +if [ "_$owners_email" = "_" ]; then + print "Owner's Email address:" + print "(Should be the same as username. This script will supply the @cup.hp.com)" + print "> \c" + read owners_email + if [ "_$owners_email" = "_" ]; then + owners_email=Unknown + fi +fi + +if [ "_$owners_extension" = "_" ]; then + print "Owner's Phone extention:" + print "(Should be of the format 7-XXXX This script will prepend \"t-44\" to" + print "the entered extension)" + print "> \c" + read owners_extension + if [ "_$owners_extension" = "_" ]; then + owners_extension=7-XXXX + fi +fi + +until [ "_$new_machine_name" != "_" ]; do + new_machine_name="garbage" + print "New machine name:" + print "> \c" + read new_machine_name + + if [ "_$new_machine_name" = "_" ]; then + error "Must enter a new machine name" + fi +done + +display_options + +display +display "Continue Installation (Y/n)?\c" +answer=y +read answer +case "$answer" in + y|Y|yes|Yes|YES|"") + continue + ;; + *) + display "Installation aborted. Rerun $me if you wish to install again" + exit 1 + ;; +esac + +function do_installation { +#display_options + +banner $new_machine_name > /etc/motd +echo $unames $unamen $unamer $unamev $unamem $unamei $unamel >> /etc/motd +cat >> /etc/motd <<:END + +******************************************************************************* +* This is a private system operated for the Hewlett-Packard Company business. * +* Authorization from HP management is required to use this system. * +* Use by unauthorized persons is prohibited. * +******************************************************************************* +For System Support: Mon-Fri 8:00-5:00 Email (site-ux@cup.hp.com) +Phone: t-447-1212 After hours/weekend Pre-arrange: t-447-0629 +------------------------------------------------------------------------------- +Usage: $machine_usage +Owner: $owners_fullname ($owners_email@cup.hp.com) Phone: +t-44$owners_extension +Location: $location +------------------------------------------------------------------------------- +:END + +display "/etc/motd successfully created" + +} # do_installation + +do_installation | tee $logfile diff --git a/clients/HP/bin/make_resolv_conf b/clients/HP/bin/make_resolv_conf new file mode 100644 index 0000000..e01a91c --- /dev/null +++ b/clients/HP/bin/make_resolv_conf @@ -0,0 +1,49 @@ +#!/bin/ksh +################################################################################ +# +# File: make_resolv_conf +# Description: A script to create a valid /etc/resolv.conf +# Author: Andrew DeFaria, California Language Labs +# Created: Wed Jan 15 16:52:22 PST 1997 +# Modified: Wed Jan 15 16:52:22 PST 1997 (Andrew DeFaria) defaria@cup.hp.com +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) + +function usage { + print -u2 "$me: [ ] [ ]" + exit 1 +} # usage + +# Get parameters +primary_dns="15.28.98.95 # smildon" +secondary_dns="15.0.96.86 # masher" +trierary_dns="15.13.168.80 # hparch4" + +if [ $# -eq 2 ]; then + primary_dns="$1" + secondary_dns="$2" +elif [ $# -eq 1 ]; then + primary_dns="$1" +fi + +resolv_conf_file=/etc/resolv.conf + +# Don't copy this time... +#cp $resolv_conf_file $resolv_conf_file.old + +print "domain cup.hp.com" > $resolv_conf_file +print "search cup.hp.com hp.com ch.apollo.hp.com" >> $resolv_conf_file +print "nameserver $primary_dns" >> $resolv_conf_file +print "nameserver $secondary_dns" >> $resolv_conf_file +print "nameserver $trierary_dns" >> $resolv_conf_file + +# If machine runs cupmail (see root crontab) then we need to make a .local +# file. +cp $resolv_conf_file $resolv_conf_file.local + +print "$me: Created new $resolv_conf_file" +#print "$me: Saved old $resolv_conf_file in $resolv_conf_file.old" diff --git a/clients/HP/bin/makehome b/clients/HP/bin/makehome new file mode 100644 index 0000000..9809c83 --- /dev/null +++ b/clients/HP/bin/makehome @@ -0,0 +1,160 @@ +#!/bin/ksh +################################################################################ +# +# File: makehome +# Description: Makes a users home directory +# Author: Andrew@DeFaria.com +# Created: Thu Jun 3 17:21:24 PDT 1999 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +function usage { + display "$me: Usage: makehome [ -v|erbose ] [ -d|ebug ] -username + +\t\t\t -uid " + + exit 1 +} # usage + +function prompt_for_field { + fieldname="$1" + fieldvalue= + + while [ ! -n "$fieldvalue" ]; do + display "Enter the value for $fieldname:\c" + read fieldvalue + + if [ ! -n "$fieldvalue" ]; then + error "Must specify $fieldname!" + fi + done +} # prompt_for_field + +function show_parms { + display "$me" + display "-------------------------------------" + display "username = $username" + display "uid = $uid" + display "homedrive = $homedrive" + display "devdrive = $devdrive" + display "homepath = $homepath" + display "devpath = $devpath" + display + display "Command line equivalent:" + display + display "$me -username $username -uid $uid" + display + display "Are the parameters correct [Y|n]?\c" + read answer + case "$answer" in + Y|y) + : OK! + ;; + *) + exit + ;; + esac +} # show_parms + +if is_not_root; then + error "You must be root to use this command" 1 +fi + +case "$(hostname)" in + dreamcicle|fudgecicle) + ;; + + *) + error "Must be running on either dreamcicle or fudgecicle to execute +this command" 2 + ;; + +esac + +# Get options +debug= +verbose= + +while [ $# -ge 1 ]; do + case "$1" in + -u|usage) + usage + ;; + + -v|-verbose) + verbose=yes + ;; + + -d|-debug) + debug=yes + ;; + + -username) + if [ $# -le 1 ]; then + error "Username not specified!" 0 + usage + fi + shift + username="$1" + ;; + + -uid) + if [ $# -le 1 ]; then + error "UID not specified!" 0 + usage + fi + shift + uid="$1" + ;; + + *) + error "Unknown option \"$1\" encountered" 0 + usage + ;; + + esac + shift +done + +if [ "$username" = "" ]; then + prompt_for_field "Username" + username=$fieldvalue +fi + +if [ "$uid" = "" ]; then + prompt_for_field "UID" + uid=$fieldvalue +fi + +homedrive=home1 +devdrive=dev1 +homepath=/netapp/dvd/$homedrive/$username +devpath=/netapp/dvd/$devdrive/$username + +show_parms + +mkdir -p $homepath +chown $uid:cdseng $homepath +chmod 775 $homepath +mkdir -p $devpath +chown $uid:cdseng $devpath +chmod 775 $devpath + +if [ ! -h $homepath/dev ]; then + ln -s /auto/dev/$username $homepath/dev +fi diff --git a/clients/HP/bin/makehosts b/clients/HP/bin/makehosts new file mode 100644 index 0000000..f100c9c --- /dev/null +++ b/clients/HP/bin/makehosts @@ -0,0 +1,44 @@ +#!/bin/ksh +################################################################################ +# +# File: makehosts +# Description: Makes an /etc/hosts file +# Author: Andrew@DeFaria.com +# Created: Thu Jun 3 17:21:24 PDT 1999 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +if [ $(/usr/xpg4/bin/id -u) -ne 0 ]; then + print -u2 "$me: Error: You must be root to use this command" + exit 1 +fi + +stdhosts=${stdhosts:-$adm_base/etc/stdhosts} +syshosts=${syshosts:-/etc/hosts} +localhosts=${localhosts:-/etc/hosts.local} + +current_ip=$(nslookup $(hostname) | tail -2 | awk '{print $NF}') + +cp $stdhosts $syshosts + +if ! grep $(hostname) $syshosts > /dev/null; then + print "$current_ip\t$(hostname).cisco.com\t\t$(hostname)" >> $syshosts +fi + +if [ -f $localhosts ]; then + cat $localhosts >> $syshosts +fi diff --git a/clients/HP/bin/mkpass b/clients/HP/bin/mkpass new file mode 100644 index 0000000..44caf97 --- /dev/null +++ b/clients/HP/bin/mkpass @@ -0,0 +1,238 @@ +#!/bin/ksh +################################################################################ +# +# File: mkpass +# Description: Mother of All (MoA) passwd administration script +# Author: Cory Chan (cory@cup.hp.com) +# Language: Korn Shell +# Modified: 11/18/1994 Ryan Fong (fong@cup.hp.com) Modified for 10.0 +# 07/26/1995 Andrew DeFaria (defaria@cup.hp.com) Revamped to use +# NFS mount point to avoid rcp. Script now works for both 9.0 +# and 10.0. +# 08/21/1995 Andrew DeFaria (defaria@cup.hp.com) Revamped mail +# message sending. +# 03/25/98 Michael Coulter (coulter) Changed "ch.apollo" to +# "che.hp.com" because of a domain name change for Chelmsford. +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# First source the appserver script +if [ -x /app/appserver ]; then + . /app/appserver +fi + +if [ "$OS" = "09" ]; then + /bin/cp /usr/spool/cron/crontabs/root /tmp/root-crontab + /usr/bin/crontab /tmp/root-crontab >> /nisclient.log 2>&1 + /bin/rm /tmp/root-crontab +else + /bin/cp /var/spool/cron/crontabs/root /tmp/root-crontab + /usr/bin/crontab /tmp/root-crontab >> /nisclient.log 2>&1 + /bin/rm /tmp/root-crontab +fi +exit + +# Set ADMIN_ROOT +ADMIN_ROOT=/app/admin + +# Check for force flag +FORCE="False" + +if [ "$1" = "-f" ]; then + FORCE="True" +fi + +# Whom to notify of problems. +NOTIFY=root@$(hostname) + +# Determine OS level +OS=`uname -r | cut -c 3-4` + +MASTER_PASSWD=$ADMIN_ROOT/lib/master_passwd +MASTER_PASSWD_MLL=$ADMIN_ROOT/lib/master_passwd.mll +LOCAL_PASSWD=/etc/passwd.loc +EXCLUDED_PASSWD=/etc/passwd.exc +MARKER_FILE=/etc/pass.time +PASSWD_OLD=/etc/passwd.old +PASSWD=/etc/passwd +TMP_PASSWD=/tmp/passwd.$$ +TMP_PASSWD2=/tmp/passwd2.$$ + +# Log and save old messages if there were any problems +MESSAGE_FILE=$ADMIN_ROOT/log/mkpass.$(uname -n) + +# Set file attribute +umask 022 +# Check for existance of $MASTER_PASSWD file. If missing send message and +# abort. +if [[ ! -f $MASTER_PASSWD ]]; then + mailx -s "mkpass: $MASTER_PASSWD file is missing!" $NOTIFY < /dev/null + exit 1 +fi + +# Check existence of necessary files; make when necessary. +if [[ ! -f $MARKER_FILE ]]; then + # make time marker if not exists + touch $MARKER_FILE +fi + +if [[ ! -f $EXCLUDED_PASSWD ]]; then + echo "# one login per line, no space/tab/null line#" > $EXCLUDED_PASSWD +fi + +if [[ ! -f $PASSWD_OLD ]]; then + # make old passwd file if not exists + cp $PASSWD $PASSWD_OLD +fi + +if [[ ! -f $LOCAL_PASSWD ]]; then + # no local file, notify and exit + cat > $MESSAGE_FILE < $MESSAGE_FILE < = /etc/passwd.old) +----------------------------------------------------------------------------- +!EOM + diff $PASSWD $PASSWD_OLD >> $MESSAGE_FILE + mailx -s "mkpass: $PASSWD incorrectly changed" $NOTIFY < $MESSAGE_FILE + exit 0 + fi +fi + +# Check to see if $LOCAL_PASSWD, $MASTER_PASSWD or $EXCLUDED_PASSWD is newer +# than $PASSWD. If so, combine $LOCAL_PASSWD and $MASTER_PASSWD (excluding +# entries from $EXCLUDED_PASSWD) to form new $PASSWD +if [[ $FORCE = "True" || + $LOCAL_PASSWD -nt $PASSWD || + $MASTER_PASSWD -nt $PASSWD || + $MASTER_PASSWD_MLL -nt $PASSWD || + $EXCLUDED_PASSWD -nt $PASSWD ]]; then + + # If only the $MASTER_PASSWD changed then make a note not to send email + if [[ $LOCAL_PASSWD -nt $PASSWD || + $EXCLUDED_PASSWD -nt $PASSWD ]]; then + NOTIFY_OF_CHANGE=True + else + NOTIFY_OF_CHANGE=False + fi + + # Save an old copy around + cp $PASSWD $PASSWD_OLD + + # Check root entry in $LOCAL_PASSWD + if grep -v "^#" $LOCAL_PASSWD | head -n 1 | grep "^root:" > /dev/null; then + # 1st entry root OKAY in $LOCAL_PASSWD + : + else + # 1st entry NOT root in passwd.loc + cat > $MESSAGE_FILE <> $TMP_PASSWD2 + done < $MASTER_PASSWD_MLL + + cat $LOCAL_PASSWD $MASTER_PASSWD $TMP_PASSWD2 > $TMP_PASSWD + + # Do exclusion + grep -v "^#" $EXCLUDED_PASSWD |\ + grep -vf $EXCLUDED_PASSWD $TMP_PASSWD > $TMP_PASSWD2 + + # Transform password file to 10.0 format + if [ $OS = "10" ]; then + sed -e 's/:\/nfs/:\/net/' -e 's/:\/bin/:\/usr\/bin/' \ + $TMP_PASSWD2 > $TMP_PASSWD + rm -f $TMP_PASSWD2 + else + mv $TMP_PASSWD2 $TMP_PASSWD + fi + + if [ -s $TMP_PASSWD ]; then + mv $TMP_PASSWD $PASSWD + chmod 444 $PASSWD + else + rm -f $TMP_PASSWD + mailx -s "mkpass: Error: Zero length passwd file resulted!" $NOTIFY < $MESSAGE_FILE < = /etc/passwd.old) +----------------------------------------------------------------------------- +!EOM + diff $PASSWD $PASSWD_OLD >> $MESSAGE_FILE + mailx -s "mkpass: Made new $PASSWD" $NOTIFY < $MESSAGE_FILE + fi +fi + +# Update marker file +touch -ma $MARKER_FILE + +# Update log file +echo "$PASSWD on `uname -n` is up to date as of `date`" > $MESSAGE_FILE + +exit 0 diff --git a/clients/HP/bin/mkpass-nisclient b/clients/HP/bin/mkpass-nisclient new file mode 100644 index 0000000..3c33961 --- /dev/null +++ b/clients/HP/bin/mkpass-nisclient @@ -0,0 +1,234 @@ +#!/bin/ksh +################################################################################ +# +# File: mkpass +# Description: Mother of All (MoA) passwd administration script +# Author: Cory Chan (cory@cup.hp.com) +# Language: Korn Shell +# Modified: 11/18/1994 Ryan Fong (fong@cup.hp.com) Modified for 10.0 +# 07/26/1995 Andrew DeFaria (defaria@cup.hp.com) Revamped to use +# NFS mount point to avoid rcp. Script now works for both 9.0 +# and 10.0. +# 08/21/1995 Andrew DeFaria (defaria@cup.hp.com) Revamped mail +# message sending. +# 03/25/98 Michael Coulter (coulter) Changed "ch.apollo" to +# "che.hp.com" because of a domain name change for Chelmsford. +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# First source the appserver script +if [ -x /app/appserver ]; then + . /app/appserver +fi + +if [ "$OS" = "09" ]; then + /app/admin/bin/nisclient-9.x +else + /app/admin/bin/nisclient +fi +exit + +# Set ADMIN_ROOT +ADMIN_ROOT=/app/admin + +# Check for force flag +FORCE="False" + +if [ "$1" = "-f" ]; then + FORCE="True" +fi + +# Whom to notify of problems. +NOTIFY=root@$(hostname) + +# Determine OS level +OS=`uname -r | cut -c 3-4` + +MASTER_PASSWD=$ADMIN_ROOT/lib/master_passwd +MASTER_PASSWD_MLL=$ADMIN_ROOT/lib/master_passwd.mll +LOCAL_PASSWD=/etc/passwd.loc +EXCLUDED_PASSWD=/etc/passwd.exc +MARKER_FILE=/etc/pass.time +PASSWD_OLD=/etc/passwd.old +PASSWD=/etc/passwd +TMP_PASSWD=/tmp/passwd.$$ +TMP_PASSWD2=/tmp/passwd2.$$ + +# Log and save old messages if there were any problems +MESSAGE_FILE=$ADMIN_ROOT/log/mkpass.$(uname -n) + +# Set file attribute +umask 022 +# Check for existance of $MASTER_PASSWD file. If missing send message and +# abort. +if [[ ! -f $MASTER_PASSWD ]]; then + mailx -s "mkpass: $MASTER_PASSWD file is missing!" $NOTIFY < /dev/null + exit 1 +fi + +# Check existence of necessary files; make when necessary. +if [[ ! -f $MARKER_FILE ]]; then + # make time marker if not exists + touch $MARKER_FILE +fi + +if [[ ! -f $EXCLUDED_PASSWD ]]; then + echo "# one login per line, no space/tab/null line#" > $EXCLUDED_PASSWD +fi + +if [[ ! -f $PASSWD_OLD ]]; then + # make old passwd file if not exists + cp $PASSWD $PASSWD_OLD +fi + +if [[ ! -f $LOCAL_PASSWD ]]; then + # no local file, notify and exit + cat > $MESSAGE_FILE < $MESSAGE_FILE < = /etc/passwd.old) +----------------------------------------------------------------------------- +!EOM + diff $PASSWD $PASSWD_OLD >> $MESSAGE_FILE + mailx -s "mkpass: $PASSWD incorrectly changed" $NOTIFY < $MESSAGE_FILE + exit 0 + fi +fi + +# Check to see if $LOCAL_PASSWD, $MASTER_PASSWD or $EXCLUDED_PASSWD is newer +# than $PASSWD. If so, combine $LOCAL_PASSWD and $MASTER_PASSWD (excluding +# entries from $EXCLUDED_PASSWD) to form new $PASSWD +if [[ $FORCE = "True" || + $LOCAL_PASSWD -nt $PASSWD || + $MASTER_PASSWD -nt $PASSWD || + $MASTER_PASSWD_MLL -nt $PASSWD || + $EXCLUDED_PASSWD -nt $PASSWD ]]; then + + # If only the $MASTER_PASSWD changed then make a note not to send email + if [[ $LOCAL_PASSWD -nt $PASSWD || + $EXCLUDED_PASSWD -nt $PASSWD ]]; then + NOTIFY_OF_CHANGE=True + else + NOTIFY_OF_CHANGE=False + fi + + # Save an old copy around + cp $PASSWD $PASSWD_OLD + + # Check root entry in $LOCAL_PASSWD + if grep -v "^#" $LOCAL_PASSWD | head -n 1 | grep "^root:" > /dev/null; then + # 1st entry root OKAY in $LOCAL_PASSWD + : + else + # 1st entry NOT root in passwd.loc + cat > $MESSAGE_FILE <> $TMP_PASSWD2 + done < $MASTER_PASSWD_MLL + + cat $LOCAL_PASSWD $MASTER_PASSWD $TMP_PASSWD2 > $TMP_PASSWD + + # Do exclusion + grep -v "^#" $EXCLUDED_PASSWD |\ + grep -vf $EXCLUDED_PASSWD $TMP_PASSWD > $TMP_PASSWD2 + + # Transform password file to 10.0 format + if [ $OS = "10" ]; then + sed -e 's/:\/nfs/:\/net/' -e 's/:\/bin/:\/usr\/bin/' \ + $TMP_PASSWD2 > $TMP_PASSWD + rm -f $TMP_PASSWD2 + else + mv $TMP_PASSWD2 $TMP_PASSWD + fi + + if [ -s $TMP_PASSWD ]; then + mv $TMP_PASSWD $PASSWD + chmod 444 $PASSWD + else + rm -f $TMP_PASSWD + mailx -s "mkpass: Error: Zero length passwd file resulted!" $NOTIFY < $MESSAGE_FILE < = /etc/passwd.old) +----------------------------------------------------------------------------- +!EOM + diff $PASSWD $PASSWD_OLD >> $MESSAGE_FILE + mailx -s "mkpass: Made new $PASSWD" $NOTIFY < $MESSAGE_FILE + fi +fi + +# Update marker file +touch -ma $MARKER_FILE + +# Update log file +echo "$PASSWD on `uname -n` is up to date as of `date`" > $MESSAGE_FILE + +exit 0 diff --git a/clients/HP/bin/mkpass.prenis b/clients/HP/bin/mkpass.prenis new file mode 100644 index 0000000..c2b484c --- /dev/null +++ b/clients/HP/bin/mkpass.prenis @@ -0,0 +1,227 @@ +#!/bin/ksh +################################################################################ +# +# File: mkpass +# Description: Mother of All (MoA) passwd administration script +# Author: Cory Chan (cory@cup.hp.com) +# Language: Korn Shell +# Modified: 11/18/1994 Ryan Fong (fong@cup.hp.com) Modified for 10.0 +# 07/26/1995 Andrew DeFaria (defaria@cup.hp.com) Revamped to use +# NFS mount point to avoid rcp. Script now works for both 9.0 +# and 10.0. +# 08/21/1995 Andrew DeFaria (defaria@cup.hp.com) Revamped mail +# message sending. +# 03/25/98 Michael Coulter (coulter) Changed "ch.apollo" to +# "che.hp.com" because of a domain name change for Chelmsford. +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# First source the appserver script +if [ -x /app/appserver ]; then + . /app/appserver +fi + +# Set ADMIN_ROOT +ADMIN_ROOT=/app/admin + +# Check for force flag +FORCE="False" + +if [ "$1" = "-f" ]; then + FORCE="True" +fi + +# Whom to notify of problems. +NOTIFY=root@$(hostname) + +# Determine OS level +OS=`uname -r | cut -c 3-4` + +MASTER_PASSWD=$ADMIN_ROOT/lib/master_passwd +MASTER_PASSWD_MLL=$ADMIN_ROOT/lib/master_passwd.mll +LOCAL_PASSWD=/etc/passwd.loc +EXCLUDED_PASSWD=/etc/passwd.exc +MARKER_FILE=/etc/pass.time +PASSWD_OLD=/etc/passwd.old +PASSWD=/etc/passwd +TMP_PASSWD=/tmp/passwd.$$ +TMP_PASSWD2=/tmp/passwd2.$$ + +# Log and save old messages if there were any problems +MESSAGE_FILE=$ADMIN_ROOT/log/mkpass.$(uname -n) + +# Set file attribute +umask 022 +# Check for existance of $MASTER_PASSWD file. If missing send message and +# abort. +if [[ ! -f $MASTER_PASSWD ]]; then + mailx -s "mkpass: $MASTER_PASSWD file is missing!" $NOTIFY < /dev/null + exit 1 +fi + +# Check existence of necessary files; make when necessary. +if [[ ! -f $MARKER_FILE ]]; then + # make time marker if not exists + touch $MARKER_FILE +fi + +if [[ ! -f $EXCLUDED_PASSWD ]]; then + echo "# one login per line, no space/tab/null line#" > $EXCLUDED_PASSWD +fi + +if [[ ! -f $PASSWD_OLD ]]; then + # make old passwd file if not exists + cp $PASSWD $PASSWD_OLD +fi + +if [[ ! -f $LOCAL_PASSWD ]]; then + # no local file, notify and exit + cat > $MESSAGE_FILE < $MESSAGE_FILE < = /etc/passwd.old) +----------------------------------------------------------------------------- +!EOM + diff $PASSWD $PASSWD_OLD >> $MESSAGE_FILE + mailx -s "mkpass: $PASSWD incorrectly changed" $NOTIFY < $MESSAGE_FILE + exit 0 + fi +fi + +# Check to see if $LOCAL_PASSWD, $MASTER_PASSWD or $EXCLUDED_PASSWD is newer +# than $PASSWD. If so, combine $LOCAL_PASSWD and $MASTER_PASSWD (excluding +# entries from $EXCLUDED_PASSWD) to form new $PASSWD +if [[ $FORCE = "True" || + $LOCAL_PASSWD -nt $PASSWD || + $MASTER_PASSWD -nt $PASSWD || + $MASTER_PASSWD_MLL -nt $PASSWD || + $EXCLUDED_PASSWD -nt $PASSWD ]]; then + + # If only the $MASTER_PASSWD changed then make a note not to send email + if [[ $LOCAL_PASSWD -nt $PASSWD || + $EXCLUDED_PASSWD -nt $PASSWD ]]; then + NOTIFY_OF_CHANGE=True + else + NOTIFY_OF_CHANGE=False + fi + + # Save an old copy around + cp $PASSWD $PASSWD_OLD + + # Check root entry in $LOCAL_PASSWD + if grep -v "^#" $LOCAL_PASSWD | head -n 1 | grep "^root:" > /dev/null; then + # 1st entry root OKAY in $LOCAL_PASSWD + : + else + # 1st entry NOT root in passwd.loc + cat > $MESSAGE_FILE <> $TMP_PASSWD2 + done < $MASTER_PASSWD_MLL + + cat $LOCAL_PASSWD $MASTER_PASSWD $TMP_PASSWD2 > $TMP_PASSWD + + # Do exclusion + grep -v "^#" $EXCLUDED_PASSWD |\ + grep -vf $EXCLUDED_PASSWD $TMP_PASSWD > $TMP_PASSWD2 + + # Transform password file to 10.0 format + if [ $OS = "10" ]; then + sed -e 's/:\/nfs/:\/net/' -e 's/:\/bin/:\/usr\/bin/' \ + $TMP_PASSWD2 > $TMP_PASSWD + rm -f $TMP_PASSWD2 + else + mv $TMP_PASSWD2 $TMP_PASSWD + fi + + if [ -s $TMP_PASSWD ]; then + mv $TMP_PASSWD $PASSWD + chmod 444 $PASSWD + else + rm -f $TMP_PASSWD + mailx -s "mkpass: Error: Zero length passwd file resulted!" $NOTIFY < $MESSAGE_FILE < = /etc/passwd.old) +----------------------------------------------------------------------------- +!EOM + diff $PASSWD $PASSWD_OLD >> $MESSAGE_FILE + mailx -s "mkpass: Made new $PASSWD" $NOTIFY < $MESSAGE_FILE + fi +fi + +# Update marker file +touch -ma $MARKER_FILE + +# Update log file +echo "$PASSWD on `uname -n` is up to date as of `date`" > $MESSAGE_FILE + +exit 0 diff --git a/clients/HP/bin/mkpty b/clients/HP/bin/mkpty new file mode 100644 index 0000000..79b7e2b --- /dev/null +++ b/clients/HP/bin/mkpty @@ -0,0 +1,102 @@ +#!/bin/ksh +# usage: mkpty -n number [-d dir] [ -m mastermajor ] + +# Algorithm: +# For master side, the files have a ptym/pty prefix. +# For slave side, the files have a pty/tty prefix. +# The suffix naming convention is: +# First 400: pty[p-za-ce-o] +# Next 2500: pty[p-za-ce-o] +# Last 25000: pty[p-za-ce-o] + +export OS=$(/bin/uname -r | /usr/bin/cut -f2 -d.) + +if [ "$OS" = "10" ]; then + mknod=/sbin/mknod +else + mknod=/etc/mknod +fi + +function makepty { + [[ -c $1 ]] && return 0 + $mknod $1 c $2 $3 + chmod 666 $1 && chown bin:bin $1 +} + +typeset -i major=16 slave +typeset -i npty=0 minor=0 j k +typeset -Z2 n2 +typeset -Z3 n3 +typeset -l prefix[25] hex[16] + +set -A prefix p q r s t u v w x y z a b c e f g h i j k l m n o +set -A hex 0 1 2 3 4 5 6 7 8 9 a b c d e f + +dir=/dev + +while getopts :n:Dd:m: c; do + case $c in + n) npty=$OPTARG;; + d) dir=$OPTARG;; + m) major=$OPTARG;; + D) set -x;; + :) print -u2 $OPTARG requires a value; exit 1;; + *) print -u2 Invalid argument $OPTARG; exit 1;; + esac +done + +shift OPTIND-1; + +if [[ $# -ne 0 ]]; then + print -u2 "Usage: $0 -n number [-d dir] [-m mastermajor]" + exit 1; +fi + +slave=major+1 +cd ${dir} + +mkdir -p pty ptym + +j=0;k=0 +while (( j < 25 && npty>0 )); do + name=${prefix[j]}${hex[k]} + makepty ptym/pty${name} ${major} ${minor} + makepty pty/tty${name} ${slave} ${minor} + if (( j<3 )); then + rm -f pty${name} && ln ptym/pty${name} pty${name} + rm -f tty${name} && ln pty/tty${name} tty${name} + fi + npty=npty-1 + (( (k=k+1) >= 16 )) && k=0 && j=j+1 + minor=minor+1 +done + +j=0;k=0 +while (( j < 25 && npty>0 )); do + n2=$k + name=${prefix[j]}${n2} + makepty ptym/pty${name} ${major} ${minor} + makepty pty/tty${name} ${slave} ${minor} + npty=npty-1 + (( (k=k+1) >= 100 )) && k=0 && j=j+1 + minor=minor+1 +done + +j=0;k=0 +while (( j < 25 && npty>0 )); do + n3=$k + name=${prefix[j]}${n3} + makepty ptym/pty${name} ${major} ${minor} + makepty pty/tty${name} ${slave} ${minor} + npty=npty-1 + (( (k=k+1) >= 1000 )) && k=0 && j=j+1 + minor=minor+1 +done + +if hp9000s700; then +: +elif hp9000s800; then + mknod ptym/clone c ${major} 0xffffff + chmod 666 ptym/clone + chown bin:bin ptym/clone +fi diff --git a/clients/HP/bin/monitor_taskbroker b/clients/HP/bin/monitor_taskbroker new file mode 100644 index 0000000..7c30ffe --- /dev/null +++ b/clients/HP/bin/monitor_taskbroker @@ -0,0 +1,39 @@ +#!/bin/ksh +################################################################################ +# +# File: monitor_taskbroker +# Description: Quick script to monitor the taskbroker/build pool +# Author: Andrew DeFaria (defaria@cup.hp.com) +# Language: Korn Shell +# Modified:: +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) + +integer day_of_week=$(date +%w) +integer sleep_time=5*60 + +talktotaskbroker=/usr/eclipse/etc/talktotaskbroker +logfile=/tmp/taskbroker$day_of_week.log + +function get_status { + print "At $(date)" + $talktotaskbroker mstatus +} # get_status + +rm -f $logfile + +print "$me started" +while true; do + if [ $day_of_week -ne $(date +%w) ]; then + day_of_week=$(date +%w) + logfile_old=$logfile + logfile=/tmp/taskbroker$day_of_week.log + print "Continuing in $logfile..." >> $logfile_old + fi + + get_status >> $logfile + sleep $sleep_time +done & diff --git a/clients/HP/bin/mount_nfs b/clients/HP/bin/mount_nfs new file mode 100644 index 0000000..59766fb --- /dev/null +++ b/clients/HP/bin/mount_nfs @@ -0,0 +1,43 @@ +#!/bin/ksh +################################################################################ +# +# File: unmount_nfs +# RCS: $Header:$ +# Description: A script to unmount all nfs mounts. Note if the automounter is +# running then this script will first shutdown the automounter. +# This script returns 0 for success or non zero if it was unable +# to umount all nfs mounts. This script must run as root. +# Author: Andrew DeFaria, California Language Labs +# Created: Fri Jun 6 10:31:51 PDT 1997 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +export me=`basename $0` +export OS=$(/bin/uname -r | /usr/bin/cut -f2 -d.) + +if [ $(id -u) -ne 0 ]; then + print -u2 "$me: Error: You must be root to use this command" + exit 1 +fi + +# First try to mount any nfs mounts listed in /etc/checklist +print "Re-establishing all static NFS mounts..." +if [ "$OS" = "10" ]; then + /usr/sbin/mount -aQF nfs +else + /etc/mount -at nfs +fi + +# Restart automounter if necessary +if [ -f /etc/automounter_was_here ]; then + print "Restarting automounter..." + if [ "$OS" = "10" ]; then + /usr/sbin/automount -f /etc/auto_master + else + /usr/etc/automount -f /etc/auto_master + fi + rm -f /etc/automounter_was_here +fi diff --git a/clients/HP/bin/mount_project_lvm b/clients/HP/bin/mount_project_lvm new file mode 100644 index 0000000..6fd51d8 --- /dev/null +++ b/clients/HP/bin/mount_project_lvm @@ -0,0 +1,31 @@ +#!/bin/ksh +################################################################################ +# +# File: mount_project_lvm +# Description: This script will mount all project's logical volumes +# Author: Andrew DeFaria (defaria@cup.hp.com) +# Language: Korn Shell +# Modifications: +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) +if [ $(id -u) -ne 0 ]; then + print -u2 "$me: Error: You must be root to execute this command!" + exit 1 +fi + +if [ $# -ne 2 ]; then + print -u2 "Usage: $me projectID fileserver" + exit 1 +fi + +projectID=$1 +fileserver=$2 + +print Mounting $projectID LVMs on $fileserver and build servers... +/app/admin/bin/buildservers "/app/admin/bin/mountlvm $projectID $fileserver" + +print Mounting $projectID and $fileserver on virtual workstations... +/app/admin/bin/wrkservers "/app/admin/bin/mountlvm $projectID $fileserver" diff --git a/clients/HP/bin/mountlvm b/clients/HP/bin/mountlvm new file mode 100644 index 0000000..0c521a8 --- /dev/null +++ b/clients/HP/bin/mountlvm @@ -0,0 +1,54 @@ +#!/bin/ksh +################################################################################ +# +# File: mountlvm +# Description: This script will mount a project's logical volumes +# Author: Andrew DeFaria (defaria@cup.hp.com) +# Language: Korn Shell +# Modifications: +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) +if [ $(id -u) -ne 0 ]; then + print -u2 "$me: Error: You must be root to execute this command!" + exit 1 +fi + +if [ $# -ne 2 ]; then + print -u2 "Usage: $me projectID fileserver" + exit 1 +fi + +projectID=$1 +fileserver=$2 + +function create_dir_and_mount { + machine=$1 + mount_directory=$2 + mount_over_directory=/tmp_mnt/net/$machine$2 + if [ ! -d $mount_over_directory ]; then + print Making $mount_over_directory + mkdir -p $mount_over_directory + fi + + if [ ! -d $mount_over_directory/lost+found ]; then + print Mounting $machine:$mount_directory to $mount_over_directory + /usr/sbin/mount $machine:$mount_directory $mount_over_directory + fi +} # create_dir_and_mount + +function kick_automounter { + automount_pid=$(/app/sj automount | awk '{print $2}') + + print Kicking automounter \($automount_pid\) + kill -HUP $automount_pid +} # kick_automounter + +create_dir_and_mount cllvu01 /CLO/Storage/Views/$projectID +create_dir_and_mount cllvu02 /CLO/Storage/Views/$projectID +create_dir_and_mount cllvu03 /CLO/Storage/Views/$projectID +create_dir_and_mount $fileserver /data/proj/$projectID + +kick_automounter diff --git a/clients/HP/bin/nisclient-11.x b/clients/HP/bin/nisclient-11.x new file mode 100644 index 0000000..f9533c3 --- /dev/null +++ b/clients/HP/bin/nisclient-11.x @@ -0,0 +1,152 @@ +#!/bin/ksh +############################################################################### +# This script sets a machine up to be an NIS client. +# Kevin Lister 10.28.98 +############################################################################### + +# Variables +logfile=/nisclient.log +me=${0##*/} + +admin=cll-support@cup.hp.com +nisdomain=cll +domainname= +namesvrs=/etc/rc.config.d/namesvrs +nsswitch=/etc/nsswitch.conf +pwfile=/etc/passwd +lclpwfile=/etc/passwd.loc +nispwfile=/etc/passwd.nis +crontab=/var/spool/cron/crontabs/root +newcrontab=/tmp/root-crontab +null=/dev/null +ypdir=/var/yp + +# Functions +function error { + print -u2 "$me: Error: $1" + /usr/bin/mailx -s "$(uname -n): NIS setup error: $1" $admin < $null + #exit 255; +} # error + +function warning { + print -u2 "$me: Warning: $1" +} # warning + +function display { + print "$1" +} # display + +function info { + display "$me: Info: $1" +} # info + +## +########## Main +## + +## +########## Check the status of the system before proceeding +## + +# Must be root to run this +if [ $(id -u) -ne 0 ]; then + error "Must be root to execute this command... Exiting." +fi + +# Check to see if this script has already been ran +if [ -a $logfile ]; then + error "$me has already been ran on $(uname -n)... Exiting" +fi + +# Check to see if domainname has been set +domainname=`/bin/domainname` +if [ "_$domainname" != "_" ]; then + error "NIS Domain name is already set to -> $domainname... Exiting." +fi + +# Are we already running NIS? +/usr/bin/ypwhich >> $logfile 2>&1 +if [ $? -eq 0 ]; then + error "This system is already running NIS... Exiting." +fi + +# Check to see if there is a name service switch config file +if [ -a $nsswitch ]; then + error "This system has a name service switch config file... Exiting." +fi + +# Check to see if /var/yp exists +if [ ! -a $ypdir ]; then + error "Directory /var/yp does not exist... Exiting" +fi + +## +########## System checks out. Set up the files and start NIS. +## + +# Set the NIS domain name - This is probably not needed +/bin/domainname $nisdomain >> $logfile 2>&1 +if [ $? -ne 0 ]; then + error "Could not set NIS domain name... Exiting." +fi + +# Setup the /etc/rc.config.d/namesvrs file +/usr/sbin/ch_rc -ap NIS_CLIENT=1 \ + -p NIS_DOMAIN=cll \ + -p YPBIND_OPTIONS="-ypsetme" + +# Email us if there are local passwd entries other than root +if [ $( /bin/wc -l $lclpwfile | /bin/cut -d " " -f 1) != "1" ]; then + /usr/bin/mailx -s "$(uname -n): passwd.loc has more than 1 line" $admin < $lclpwfile +fi + +# Create NIS passwd file +if [ -a $lclpwfile ]; then + /bin/grep "^root" $lclpwfile > $nispwfile +else + /bin/grep "^root" $pwfile > $nispwfile +fi + +/bin/cat >> $nispwfile <<:END +adm:*:4:4::/usr/adm:/usr/bin/sh +anon:*:21:5:placeholder for future:/:/usr/bin/sync +bin:*:2:2::/usr/bin:/bin/sh +daemon:*:1:5::/:/usr/bin/sh +ftp:*:500:10:anonymous ftp:/home/ftp:/usr/bin/false +lp:*:9:7:[Line Printer]:/usr/spool/lp:/usr/bin/sh +nuucp:*:6:1:0000-uucp(0000):/usr/spool/uucppublic:/usr/lib/uucp/uucico +sync:*:20:1::/:/usr/bin/sync +tftp:*:510:1:Trivial FTP user:/usr/tftpdir:/usr/bin/false +uucp:*:5:3::/usr/spool/uucppublic:/usr/lib/uucp/uucico +who:*:90:1::/:/usr/bin/who +:END + +/bin/grep -v "^root" $lclpwfile >> $nispwfile + +/bin/cat >> $nispwfile <<:END2 +-@dangerous-users ++@sysadmin ++@site-ux ++: +:END2 + +mv $pwfile $pwfile.preNIS +mv $nispwfile $pwfile +chmod 444 $pwfile +chown root:other $pwfile + +# Remove MoA from the root crontab +sed -e "/MoA/d" -e "/mkpass/d" < $crontab > $newcrontab +mv $crontab $crontab-preNIS +/usr/bin/crontab $newcrontab +chmod 444 $crontab +chown root:other $crontab + +# Start the NIS Client daemons +/sbin/init.d/nis.client start >> $logfile 2>&1 +if [ $? -ne 0 ]; then + error "Problem starting the NIS client daemons... Exiting." +fi + +# Email us that a machine was updated +/usr/bin/mailx -s "$(uname -n): NIS setup complete" $admin < $null diff --git a/clients/HP/bin/nisclient-9.x b/clients/HP/bin/nisclient-9.x new file mode 100644 index 0000000..e963f24 --- /dev/null +++ b/clients/HP/bin/nisclient-9.x @@ -0,0 +1,176 @@ +#!/bin/ksh +############################################################################### +# This script sets a machine up to be an NIS client. +# Kevin Lister 10.28.98 + +############################################################################### +# Variables +logfile=/nisclient.log +me=${0##*/} + +admin=cll-support@cup.hp.com +nisdomain=cll +domainname= +netnfsrc=/etc/netnfsrc +nsswitch=/etc/nsswitch.conf +pwfile=/etc/passwd +lclpwfile=/etc/passwd.loc +nispwfile=/etc/passwd.nis +crontab=/usr/spool/cron/crontabs/root +newcrontab=/tmp/root-crontab +null=/dev/null +ypdir=/usr/etc/yp + +# Functions +function error { + print -u2 "$me: Error: $1" + /usr/bin/mailx -s "$(uname -n): NIS setup error: $1" $admin < $null + exit 255; +} # error + +function warning { + print -u2 "$me: Warning: $1" +} # warning + +function display { + print "$1" +} # display + +function info { + display "$me: Info: $1" +} # info + +## +########## Main +## + +## +########## Check the status of the system before proceeding +## + +# Must be root to run this +if [ $(id -u) -ne 0 ]; then + error "Must be root to execute this command... Exiting." +fi + +# Check to see if this script has already been ran +if [ -a $logfile ]; then + error "$me has already been ran on $(uname -n)... Exiting" +fi + +# Check to see if domainname has been set +domainname=`/bin/domainname` +if [ "_$domainname" != "_" ]; then + error "NIS Domain name is already set to -> $domainname... Exiting." +fi + +# Are we already running NIS? +/usr/bin/ypwhich >> $logfile 2>&1 +if [ $? -eq 0 ]; then + error "This system is already running NIS... Exiting." +fi + +# Check to see if there is a name service switch config file +if [ -a $nsswitch ]; then + error "This system has a name service switch config file... +Exiting." +fi + +# Check to see if /etc/yp exists +if [ ! -a $ypdir ]; then + error "Directory /etc/yp does not exist... Exiting" +fi + +## +########## System checks out. Set up the files and start NIS. +## + +# Set the NIS domain name - This is probably not needed +/bin/domainname $nisdomain >> $logfile 2>&1 +if [ $? -ne 0 ]; then + error "Could not set NIS domain name... Exiting." +fi + +# Setup the /etc/netnfsrc file +mv $netnfsrc $netnfsrc.preNIS +cp $netnfsrc.preNIS $netnfsrc +sed -e "s/^NIS_CLIENT=0/NIS_CLIENT=1/" \ + -e "s/^NISDOMAIN=/NISDOMAIN=cll/" \ + < $netnfsrc > $netnfsrc-tmp +mv $netnfsrc-tmp $netnfsrc +chmod 544 $netnfsrc +chown bin:bin $netnfsrc + +# Email us if there are local passwd entries other than root +if [ $( /bin/wc -l $lclpwfile | /usr/bin/cut -d " " -f 1) != "1" ]; then + /usr/bin/mailx -s "$(uname -n): passwd.loc has more than 1 line" +$admin < $lclpwfile +fi + +# Create NIS passwd file +if [ -a $lclpwfile ]; then + /bin/grep "^root" $lclpwfile > $nispwfile +else + /bin/grep "^root" $pwfile > $nispwfile +fi + +/bin/cat >> $nispwfile <<:END +adm:*:4:4:,_MoA_:/usr/adm:/bin/sh +anon:*:21:5:placeholder for future,_MoA_:/:/bin/sync +bin:*:2:2:,_MoA_:/bin:/bin/sh +daemon:*:1:5:,_MoA_:/:/bin/sh +ftp:*:24:5:Anonymous &,,,_LcL_:/home/ftp:/bin/sync +ftp:*:500:10:anonymous ftp,_MoA_:/home/ftp:/bin/false +lp:*:9:7:[Line Printer],,,,_MoA_:/usr/spool/lp:/bin/sh +nuucp:*:6:1:0000-uucp(0000),_MoA_:/usr/spool/uucppublic:/usr/lib/uucp/uucico +sync:*:20:1:,_MoA_:/:/bin/sync +tftp:*:510:1:Trivial FTP user,_MoA_:/usr/tftpdir:/bin/false +uucp:*:5:3:,_MoA_:/usr/spool/uucppublic:/usr/lib/uucp/uucico +who:*:90:1:,_MoA_:/:/bin/who +:END + +/bin/grep -v "^root" $lclpwfile >> $nispwfile + +/bin/cat >> $nispwfile <<:END2 +-@dangerous-users ++@sysadmin ++@site-ux ++: +:END2 + +mv $pwfile $pwfile.preNIS +mv $nispwfile $pwfile +chmod 444 $pwfile +chown root:other $pwfile + +# Remove MoA from the root crontab +sed -e "/MoA/d" -e "/mkpass/d" < $crontab > $newcrontab +mv $crontab $crontab-preNIS +/usr/bin/crontab $newcrontab +chmod 444 $crontab +chown root:other $crontab + +# Start the NIS Client daemons +/etc/ypbind -ypset >> $logfile 2>&1 +sleep 10 +/usr/bin/ypwhich >> $logfile 2>&1 +if [ $? -ne 0 ]; then + error "Problem starting the NIS client daemons... Exiting." +fi + +# Create /net symlink if automounter is NOT running +/bin/ps -ef | /bin/grep automount | /bin/grep -v grep >> $logfile 2>&1 +if [ $? -ne 0 ]; then + /bin/ln -s /nfs /net >> $logfile 2>&1 +fi + +# Create symlinks to shells so new passwd file doesn't blow up +/bin/ln -s /bin/sh /usr/bin/sh +/bin/ln -s /bin/csh /usr/bin/csh +/bin/ln -s /bin/ksh /usr/bin/ksh + +# Create symlinks to ypdir, not required, just friendlier +/bin/ln -s /usr/etc/yp /var/yp + +# Email us that a machine was updated +/usr/bin/mailx -s "$(uname -n): NIS setup complete" $admin < $null diff --git a/clients/HP/bin/nisclient-adl b/clients/HP/bin/nisclient-adl new file mode 100644 index 0000000..c111373 --- /dev/null +++ b/clients/HP/bin/nisclient-adl @@ -0,0 +1,166 @@ +#!/bin/ksh +############################################################################### +# This script sets a machine up to be an NIS client. +# Kevin Lister 10.28.98 +############################################################################### +# Variables +logfile=/nisclient.log +me=${0##*/} + +admin=adl-support@cup.hp.com +nisdomain=adl +domainname= +namesvrs=/etc/rc.config.d/namesvrs +nsswitch=/etc/nsswitch.conf +pwfile=/etc/passwd +lclpwfile=/etc/passwd.loc +nispwfile=/etc/passwd-nis +grpfile=/etc/group +nisgrpfile=/etc/group-nis +crontab=/var/spool/cron/crontabs/root +newcrontab=/tmp/root-crontab +null=/dev/null +ypdir=/var/yp + +# Functions +function error { + print -u2 "$me: Error: $1" + /usr/bin/mailx -s "$(uname -n): NIS setup error: $1" $admin < $null + exit 255; +} # error + +function warning { + print -u2 "$me: Warning: $1" +} # warning + +function display { + print "$1" +} # display + +function info { + display "$me: Info: $1" +} # info + +## +########## Main +## + +## +########## Check the status of the system before proceeding +## + +# Must be root to run this +if [ $(id -u) -ne 0 ]; then + error "Must be root to execute this command... Exiting." +fi + +# Check to see if this script has already been ran +if [ -a $logfile ]; then + error "$me has already been ran on $(uname -n)... Exiting" +fi + +# Check to see if domainname has been set +domainname=`/bin/domainname` +if [ "_$domainname" != "_" ]; then + error "NIS Domain name is already set to -> $domainname... Exiting." +fi + +# Are we already running NIS? +/usr/bin/ypwhich >> $logfile 2>&1 +if [ $? -eq 0 ]; then + error "This system is already running NIS... Exiting." +fi + +# Check to see if there is a name service switch config file +if [ -a $nsswitch ]; then + error "This system has a name service switch config file... +Exiting." +fi + +# Check to see if /var/yp exists +if [ ! -a $ypdir ]; then + error "Directory /var/yp does not exist... Exiting" +fi + +## +########## System checks out. Set up the files and start NIS. +## + +# Set the NIS domain name - This is probably not needed +/bin/domainname $nisdomain >> $logfile 2>&1 +if [ $? -ne 0 ]; then + error "Could not set NIS domain name... Exiting." +fi + +# Setup the /etc/rc.config.d/namesvrs file +/usr/sbin/ch_rc -ap NIS_CLIENT=1 \ + -p NIS_DOMAIN=adl \ + -p WAIT_FOR_NIS_SERVER=FALSE + +# Email us if there are local passwd entries other than root +if [ $( /bin/wc -l $lclpwfile | /bin/cut -d " " -f 1) != "1" ]; then + /usr/bin/mailx -s "$(uname -n): passwd.loc has more than 1 line" +$admin < $lclpwfile +fi + +# Create NIS passwd file +if [ -a $lclpwfile ]; then + /bin/grep "^root" $lclpwfile > $nispwfile +else + /bin/grep "^root" $pwfile > $nispwfile +fi + +/bin/cat >> $nispwfile <<:END +adm:*:4:4::/usr/adm:/usr/bin/sh +anon:*:21:5:placeholder for future:/:/usr/bin/sync +bin:*:2:2::/usr/bin:/bin/sh +daemon:*:1:5::/:/usr/bin/sh +ftp:*:500:10:anonymous ftp:/home/ftp:/usr/bin/false +lp:*:9:7:[Line Printer]:/usr/spool/lp:/usr/bin/sh +nuucp:*:6:1:0000-uucp(0000):/usr/spool/uucppublic:/usr/lib/uucp/uucico +sync:*:20:1::/:/usr/bin/sync +tftp:*:510:1:Trivial FTP user:/usr/tftpdir:/usr/bin/false +uucp:*:5:3::/usr/spool/uucppublic:/usr/lib/uucp/uucico +who:*:90:1::/:/usr/bin/who +:END + +/bin/grep -v "^root" $lclpwfile >> $nispwfile + +/bin/cat >> $nispwfile <<:END2 +-@dangerous-users ++@sysadmin ++@site-ux ++: +:END2 + +mv $pwfile $pwfile.preNIS +mv $nispwfile $pwfile +chmod 444 $pwfile +chown root:other $pwfile + +/bin/cat >> $nisgrpfile <<:GRPEND +root:*:0:root +other:*:1:root,hpdb +bin:*:2:root,bin +sys:*:3:root,uucp +adm:*:4:root,adm +daemon:*:5:root,daemon +mail:*:6:root +lp::7:root,lp +nogroup:*:-2: ++: +:GRPEND + +mv $grpfile $grpfile-preNIS +mv $nisgrpfile $grpfile +chmod 444 $grpfile +chown bin:bin $grpfile + +# Start the NIS Client daemons +/sbin/init.d/nis.client start >> $logfile 2>&1 +if [ $? -ne 0 ]; then + error "Problem starting the NIS client daemons... Exiting." +fi + +# Email us that a machine was updated +/usr/bin/mailx -s "$(uname -n): NIS setup complete" $admin < $null diff --git a/clients/HP/bin/pdl-config b/clients/HP/bin/pdl-config new file mode 100644 index 0000000..7b61a64 --- /dev/null +++ b/clients/HP/bin/pdl-config @@ -0,0 +1,446 @@ +#!/usr/bin/ksh +################################################################################ +# +# File: pdl-config +# Description: ADL system configuration script +# To run this script you must have the pdl-config.src parameter +# file located in root. It is important that the source parameter +# file be read and understood before running the script. +# See below for useful comments. +# Author: Kevin Lister - kel@cup.hp.com +# Date 3.11.99 +# Language: Korn Shell +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +# Revision History +# 3.25.99 kel Changed the name of the Clearcase install script in shell +# archive, so it had to be changed here as well. Added an +# eclipse install script to the shell archive, so a line to +# to remove it if Clearcase is not installed had to be added +# here as well. +# 4.1.99 kel Added code to determine if the installed system is going to have +# a graphics console. If yes, then the /etc/dt/config/Xservers +# file needs to have the console server line uncommented. +# Also added absolute paths to the unix commands. +# 6.22.99 kel Added "bufpages 0" to the kernel config function. This is +# required if dynamic buffer cache sizes are being used. +# +################################################################################ +# Useful (hopefully) Comments Section +# +# This script will configure a system to operate nicely in the ADL +# infrastructure. This script requires the pdl-config.src file in order to +# run. The pdl-config.src file contains variables that determine exactly +# what type of optional software to install, which patch bundle to install, +# configures various system files, etc. +# +# Here is a brief description of what this script will do: +# +# 1) Check that the script is run as root +# 2) Check that the architecture is correct. The script will run on most +# hardware. The architecture is really only important when trying to +# determine which 100Mbit drivers to install. +# 3) Sources the input parameter source file. +# 4) Determine if script is run intereactive or not. +# 5) Determine if 100Mbit drivers are to be installed +# 6) If intereactive, greet the user and display the parameter settings. +# 7) Modify the kernel system file located in /stand/system +# 8) Download the shell archive file from the anonymous ftp server and unpack. +# The shell archive contains many files and symlinks and will not be +# listed here. See the README in the ahell archive build area and the shell +# archive itself for more details. One can also look through the "root" +# directory that is used to build the archive to see which files and +# symlinks are included. +# 9) Modify the /etc/rc.config.d files. (turn off unused stuff) +# 10) Modify the /etc/issue, /etc/gettydefs and /etc/motd files. +# 11) Modify miscellaneous files. +# 12) Perform miscellaneous setup procedures: +# a) Run /net/bismol/App/admin/bin/setup +# b) Run /usr/local/bin/ninstall -h bismol lp adm net3 +# c) Run /usr/adm/netdist/netdaemon.dy +# d) Run /usr/sbin/catman -w +# 13) Set the system up for ClearCase installation upon automatic reboot. +# 14) Install optional software and patches from the specified depot server. +# +# END of Useful Comments Section +################################################################################ +# +## +### Variables +## +# + +SRC_FILE=/pdl-config.src + +BASE=${0##*/} +HOST=`/bin/uname -n` +ARCH=`/bin/uname -m` +OS=`/bin/uname -r | /usr/bin/cut -c 3-4` +integer INDEX=0 + +# +## +### Functions +## +# + +function error { + print -u2 "\t$BASE: Error: $1" +} + +function warning { + print -u2 "\t$BASE: Warning: $1" +} + +function display { + print "\t$1" +} + +function usage { + display "\t$BASE [-usage]" + display " -usage: Print this usage message" + display " " + error "$1" + exit 1 +} + +function step { + let INDEX=INDEX+1 + display "\tStep #$INDEX: $@" +} + +function get_shar { +step "Get shell archive from ftp server and unpack" + cd / + ftp -n $FTP_SERVER <<@EOD +user $FTP_USER $FTP_PASSWD +cd $SHAR_DIR +get $SHAR_FILE +quit +@EOD +if [ $? -ne 0 ]; then + error "Unable to ftp $SHAR_FILE from $FTP_SERVER" + exit 1 +fi +sh $SHAR_FILE >> $LOGFILE 2>&1 +if [ $? -ne 0 ]; then + error "Cannot unpack shell archive." + exit 1 +fi +/bin/rm -f $SHAR_FILE +} + +function clearcase_setup { + if [ "$CLEARCASE" = "no" ]; then + /bin/rm -f /sbin/rc3.d/S998install_clearcase + /bin/rm -f /sbin/rc3.d/S999install_eclipse + fi +} + +function chk_uid { + if [ $(id -u) -ne 0 ]; then + error "Must be root to execute this command... Exiting!" + exit 1 + fi +} + +function chk_arch { + case $ARCH in + 9000/7[1-3]*|9000/755|9000/7[7-8]*|9000/8**) + continue + ;; + + *) + warning "\tUnknown machine type $ARCH, Exiting!" + exit 1 + ;; + esac +} + +function read_src { + if [ -a $SRC_FILE ]; then + . $SRC_FILE + case "$CLEARCASE" in + y|Y|yes|YES|Yes|1) + CLEARCASE=yes + ;; + *) + CLEARCASE=no + ;; + esac + case "$SWINSTALL" in + y|Y|yes|YES|Yes|1) + SWINSTALL=yes + ;; + *) + SWINSTALL=no + ;; + esac + else + error "Source file does not exist!" + exit 1 + fi +} + +function set_mode { + case "$INTERACTIVE" in + y|Y|yes|YES|Yes|1) + INTERACTIVE=yes + ;; + *) + INTERACTIVE=no + ;; + esac +} + +function fast_enet { + if [ "_$ENET_DRVRS" = "_" ]; then + FAST_ENET=no + else + FAST_ENET=yes + fi +} + +function mod_kernel { + step "Modify /stand/system file." + grep -v -E 'maxswapchunks|default_disk_ir|nstrpty' /stand/system \ + > /stand/system.new + /bin/mv /stand/system /stand/system.orig + /bin/mv /stand/system.new /stand/system + + case $ARCH in + 9000/7[1-5]*) + echo "bufpages 0" >> /stand/system + echo "create_fastlinks 1" >> /stand/system + echo "dbc_max_pct 25" >> /stand/system + echo "default_disk_ir 1" >> /stand/system + echo "fs_async 1" >> /stand/system + echo "maxdsiz (256*1024*1024)" >> /stand/system + echo "maxfiles 256" >> /stand/system + echo "maxfiles_lim 2048" >> /stand/system + echo "maxssiz (80*1024*1024)" >> /stand/system + echo "maxswapchunks 4096" >> /stand/system + echo "maxuprc 500" >> /stand/system + echo "maxusers 150" >> /stand/system + echo "netmemmax 0" >> /stand/system + echo "nfile 7000" >> /stand/system + echo "nflocks 400" >> /stand/system + echo "ninode 20000" >> /stand/system + echo "nproc 1500" >> /stand/system + echo "npty 512" >> /stand/system + echo "nstrpty 512" >> /stand/system + echo "semmns 200" >> /stand/system + if [ "$OS" = "10" ]; then + echo "large_ncargs_enabled 1" >> /stand/system + fi + ;; + + 9000/7[7-8]*|9000/8**) + echo "bufpages 0" >> /stand/system + echo "create_fastlinks 1" >> /stand/system + echo "dbc_max_pct 25" >> /stand/system + echo "default_disk_ir 1" >> /stand/system + echo "fs_async 1" >> /stand/system + echo "maxdsiz (512*1024*1024)" >> /stand/system + echo "maxfiles 256" >> /stand/system + echo "maxfiles_lim 2048" >> /stand/system + echo "maxssiz (80*1024*1024)" >> /stand/system + echo "maxswapchunks 4096" >> /stand/system + echo "maxuprc 1000" >> /stand/system + echo "maxusers 256" >> /stand/system + echo "netmemmax 0" >> /stand/system + echo "nfile 14000" >> /stand/system + echo "nflocks 800" >> /stand/system + echo "ninode 40000" >> /stand/system + echo "nproc 3000" >> /stand/system + echo "npty 512" >> /stand/system + echo "nstrpty 512" >> /stand/system + echo "semmns 400" >> /stand/system + if [ "$OS" = "10" ]; then + echo "large_ncargs_enabled 1" >> /stand/system + fi + ;; + + *) + warning "Unknown machine model $ARCH!" + warning "Leaving kernel parameters as default" + /bin/mv /stand/system.orig /stand/system + ;; + esac +} # mod_kernel + +function greet { + display "\tADL System Configuration script." + display + display "\tYou are about to install and modify various system files," + display "\tinstall system patches, install optional software and," + display "\tif you elected to do so, install ClearCase 3.2." + display + display "\tIf you wish to modify the parameters below exit the install" + display "\tand modify the parameters in the $SRC_FILE file." + display + display "\tMachine Name:\t\t\t$MACHINE_NAME" + display "\tMachine Usage:\t\t\t$MACHINE_USAGE" + display "\tMacine Location:\t\t$LOCATION" + display "\tOwner's Fullname:\t\t$OWNER_NAME" + display "\tOwner's Email:\t\t\t$OWNER_EMAIL" + display "\tOwner's Extension:\t\t$OWNER_EXTENSION" + display "\tInstall ClearCase?:\t\t$CLEARCASE" + display "\tInstall 100Mbit Drivers?:\t$FAST_ENET" + if [ "$SWINSTALL" = "yes" ]; then + display "\tThe following products will be installed from $DEPOT:" + display "\t$PRODUCTS" + display + else + display + fi + if [ "$INTERACTIVE" = "yes" ]; then + display "\tContinue installation with these parameters (Y|n)?\c" + display + answer=y + read answer + case "$answer" in + y|Y|yes|Yes|YES|"") + continue + ;; + *) + display + display "\tYou have chosen NOT to run the $BASE setup script... +Exiting" + exit 1 + ;; + esac + fi +} # greet + +function mod_rc_files { + /usr/sbin/ch_rc -ap AUDIO_SERVER=0 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap LIST_TEMPS=0 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap CLEAR_TMP=1 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap HPARRAY_START_STOP=0 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap NIS_CLIENT=1 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap NIS_DOMAIN=pdl >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap START_LLBD=0 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap NTPDATE_SERVER=cupertino.ntp.hp.com >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap XNTPD=1 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap NETTL=0 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap NUM_NFSIOD=16 >> $LOGFILE 2>&1 + /usr/sbin/ch_rc -ap VTDAEMON_START=0 >> $LOGFILE 2>&1 + if [ "$OS" = "10" ]; then + /usr/sbin/ch_rc -ap WAIT_FOR_NIS_SERVER=FALSE >> $LOGFILE 2>&1 + fi +} + +function mod_etc_files { + step "/etc files setup" + print "+auto.master" > /etc/auto_master + /bin/chmod 644 /etc/auto_master + /bin/chown root:root /etc/auto_master + + sed "s/GenericSysName/$MACHINE_NAME/" /etc/issue > /etc/issue-new + /bin/mv /etc/issue /etc/issue-orig + /bin/mv /etc/issue-new /etc/issue + + sed "s/Console Login:/$MACHINE_NAME Console Login:/" /etc/gettydefs \ + > /etc/gettydefs-new + /bin/mv /etc/gettydefs /etc/gettydefs-orig + /bin/mv /etc/gettydefs-new /etc/gettydefs + + /bin/banner $MACHINE_NAME > /etc/motd + /bin/uname -a >> /etc/motd + cat >> /etc/motd <<:END + +******************************************************************************* +* This is a private system operated for the Hewlett-Packard Company +business. * +* Authorization from HP management is required to use this system. +* +* Use by unauthorized persons is prohibited. +* + +******************************************************************************* +For System Support: Mon-Fri 8:00-5:00 Email (site-ux@cup.hp.com) +Phone: t-447-1212 After hours/weekend Pre-arrange: t-447-0629 + +------------------------------------------------------------------------------- +Usage: $MACHINE_USAGE +Owner: $OWNER_NAME ($OWNER_EMAIL) Phone: $OWNER_EXTENSION +Location: $LOCATION + +------------------------------------------------------------------------------- +:END + + sed "s/Root user/Root\@$HOST/" /etc/passwd > /tmp/passwd-new + /bin/mv /tmp/passwd-new /etc/passwd +} # mod_etc_files + +function mod_misc_files { + step "Miscellaneous file setup" + /bin/rm -f /var/adm/cron/at.allow + /bin/rm -f /var/adm/cron/cron.allow + /bin/chmod 644 /dev/lan* + case "$WORKSTATION" in + y|Y|yes|YES|Yes|1) + WORKSTATION=yes + ;; + *) + WORKSTATION=no + ;; + esac + if [ "$WORKSTATION" = "yes" ]; then + /bin/sed -e "s/# \*/ \*/" Xservers > /tmp/Xservers-new + /bin/mv /tmp/Xservers-new /etc/dt/config/Xservers + /bin/chmod 444 /etc/dt/config/Xservers + /bin/chown root:other /etc/dt/config/Xservers + fi +} + +function misc_setup { + step "Setup Application Server" + /net/bismol/App/admin/bin/setup >> $LOGFILE 2>&1 + + step "Ninstalling lp, adm and net3 packages" + /usr/local/bin/ninstall -h bismol lp adm net3 >> $LOGFILE 2>&1 + + step "Run netdaemon.dy" + /usr/adm/netdist/netdaemon.dy >> $LOGFILE 2>&1 + + step "Create the whatis database" + /usr/sbin/catman -w >> $LOGFILE 2>&1 +} + +function inst_sw { + if [ "$SWINSTALL" = "yes" ]; then + step "Installing Patches and Optional Software, be patient!" + /usr/sbin/swinstall -s $DEPOT -x $OPTIONS $PRODUCTS $ENETDRVR >> +$LOGFILE 2>&1 + else + step "Rebuilding kernel with new parameters." + /usr/sbin/mk_kernel -v -o /stand/vmunix >> $LOGFILE 2>&1 + step "Rebooting the system..." + cd / + /usr/sbin/shutdown -ry 0 + fi +} + +# +## +### Main +## +# + +chk_uid +chk_arch +read_src +set_mode +fast_enet +greet +mod_kernel +get_shar +mod_rc_files +mod_etc_files +mod_misc_files +misc_setup +clearcase_setup +inst_sw diff --git a/clients/HP/bin/pdl-config.src b/clients/HP/bin/pdl-config.src new file mode 100644 index 0000000..d215d00 --- /dev/null +++ b/clients/HP/bin/pdl-config.src @@ -0,0 +1,159 @@ +################################################################################ +# +# File: pdl-config.src +# Description: Parameter Source File for the ADL system configuration script +# pdl-config. This file is required by the pdl-config script +# in order to run. +# See below for useful comments. +# Author: Kevin Lister (C) - kel@cup.hp.com +# Date: 3.11.99 +# Language: Korn Shell +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +# Revision History +# 4.1.99 kel added the WORKSTATION variable. Setting the WORKSTATION +# variable to yes will set the machine up with a graphics console +# login using CDE. +# +################################################################################ +# Useful (hopefully) Comments Section +# +# Do not make your changes to the varibales in the comment section. Change +# the variables at the end of this file. +# +# Below you will find all of the ENV variables that the pdl-config script +# will use to configure the system. +# Descriptions for these variables can be found below, read on. +# +# Set the INTERACTIVE ENV variable to yes if you wish to have a chance to view +# the configuration parameters the script will use before proceeding. Set to +# no otherwise. +# +# INTERACTIVE=yes +# +# Set the WORKSTATION variable to yes if you are installing a system that will +# have a graphics console monitor attached. Setting WORKSTATION to no disbales +# CDE on the console. +# +# WORKSTATION=no +# +# You can have ClearCase 3.2 installed automatically by setting CLEARCASE +# to yes. If you do not want ClearCase, set to no. +# +# CLEARCASE=yes +# +# If you want the Patch bundle (see below) and Optional Software installed +# set SWINSTALL to yes. +# +# SWINSTALL=yes +# +# If you plan to use a 100Mbit network interface then set FAST_ENET to yes +# to have the correct drivers installed. Set to no if you don't. +# +# FAST_ENET=yes +# +# Set the location of the logfile for the configure script using the LOGFILE +# variable. +# +# LOGFILE=/pdl-config.log +# +# The /etc/motd file will be set up with the information contained in the next +# several variables. The /etc/issue and /etc/gettydefs files will also be +# setup by using the MACHINE_NAME variable. You should set these to something +# that makes sense. +# +# OWNER_NAME="ADL Support" +# OWNER_EMAIL=pdl-support@cup.hp.com +# OWNER_EXTENSION=t-447-5790 +# MACHINE_USAGE="X Terminal Server" +# LOCATION=RDC +# MACHINE_NAME=Generic +# +# The next several variables set up the depot server name, depot path and +# names of the software bundles and products to install. It is likely that you +# you will only need to change ENET_DRVRS. Set ENET_DRVRS to 100BT-HSC for a +# J282 (780). Set ENET_DRVRS to SX00306 for a 755. The default for XTERM_SVR +# is "". If you really don't want patches and optional software then set +# PATCHES and OPTIONAL to "". You should never need to modify PRODUCTS, DEPOT +# or OPTIONS. +# +# PATCHES=Patches-Generic +# OPTIONAL="OptionalSoftware SysMonSoftware VUEtoCDE" +# XTERM_SVR="ENWARE netstation" +# ENET_DRVRS=100BT-HSC +# ENET_DRVRS=SX00306 +# PRODUCTS="$PATCHES $OPTIONAL $XTERM_SVR $ENET_DRVRS" +# DEPOT=pdliux01:/depots/10.20 +# OPTIONS="autoreboot=true" +# +# Finally, the next several variables set up the ftp server, directory and +# filename of the shell archive that the script uses to unpack all kinds +# of useful files and symlinks. You should never have to modify these. +# +# FTP_SERVER=15.0.98.138 +# FTP_USER=anonymous +# FTP_PASSWD=$LOGNAME@$(uname -n).cup.hp.com +# SHAR_DIR=productivity/pdl-config +# SHAR_FILE=pdl-config.shar +# +# END of Useful Comments Section +################################################################################ +# +## +### Change these to suit your fancy. +## +# + +INTERACTIVE=yes +WORKSTATION=no +CLEARCASE=yes +SWINSTALL=yes +FAST_ENET=no +LOGFILE=/pdl-config.log + +# +## +### /etc/motd setup +## +# + +OWNER_NAME="PDL Support" +OWNER_EMAIL=pdl-support@cup.hp.com +OWNER_EXTENSION=t-447-???? +MACHINE_USAGE="Change Me" +LOCATION=RDC +MACHINE_NAME=GENERIC + +# +## +### Patches and Optional Software +## +# + +PATCHES=Patches-VUServer +OPTIONAL="OptionalSoftware SysMonSoftware VUEtoCDE" +XTERM_SVR="" +ENET_DRVRS="" +PRODUCTS="$PATCHES $OPTIONAL $XTERM_SVR $ENET_DRVRS" + +# +## +### Depot Server Information +## +# + +DEPOT=pdliux01:/depots/10.20 +OPTIONS="autoreboot=true" + +# +## +### Shell Archive Server and Location +## +# + +FTP_SERVER=15.0.98.138 +FTP_USER=anonymous +FTP_PASSWD=$LOGNAME@$(uname -n).cup.hp.com +SHAR_DIR=productivity/pdl-config +SHAR_FILE=pdl-config.shar diff --git a/clients/HP/bin/pdl-new-passwd b/clients/HP/bin/pdl-new-passwd new file mode 100644 index 0000000..a6f13e7 --- /dev/null +++ b/clients/HP/bin/pdl-new-passwd @@ -0,0 +1,63 @@ +#!/bin/ksh +################################################################################ +# +# File: newpasswd +# Description: Change the root passwd entry in /etc/passwd +# Author: Kevin Lister kel@cup.hp.com +# Language: Korn Shell +# +# (c) Copyright 1999, Hewlett-Packard Company, all rights reserved. +# +################################################################################ +# +## +### Variables +## +# + +ADMIN=pdl-support@cup.hp.com +PWFILE=/etc/passwd +PWFILENEW=/etc/passwd-new +PWFILEOLD=/etc/passwd-old +SYSNAME=$(uname -n) +BASE=${0##*/} +WHOAMI=$(id -u) +NULL=/dev/null + +# +## +### Functions +## +# + +# Functions +function error { + print -u2 "$BASE: Error: $1" +} + +# +## +### Main +## +# + +# Must be root to run this +if [ $WHOAMI -ne 0 ]; then + error "Must be root to execute this command... Exiting." + exit 0 +fi + +# Create the new passwd file +/bin/cat >> $PWFILENEW <<:NEWPW +root:u4/rTgJX35zHg:0:1:Root@$SYSNAME:/:/sbin/sh +:NEWPW +/bin/grep -v "^root" $PWFILE >> $PWFILENEW + +# Save the old passwd file, install the new one +/bin/mv $PWFILE $PWFILEOLD +/bin/mv $PWFILENEW $PWFILE +/bin/chmod 444 $PWFILE +/bin/chown root:other $PWFILE + +# Email us that a machine was updated +/usr/bin/mailx -s "$SYSNAME: Root passwd changed!" $ADMIN < $NULL diff --git a/clients/HP/bin/pingnet b/clients/HP/bin/pingnet new file mode 100644 index 0000000..9d0383f --- /dev/null +++ b/clients/HP/bin/pingnet @@ -0,0 +1,115 @@ +#!/bin/ksh +################################################################################ +# +# File: pingnet +# RCS: $Header: pingnet,v 1.3 98/03/04 00:42:49 defaria Exp $ +# Description: A script to ping all machines and report status. This script +# uses /etc/hosts and selects only machines in the IP range of +# 15.0.96.x to 15.0.99.x. +# Author: Andrew DeFaria +# Created: Sat Oct 26 10:04:28 PDT 1996 +# Modified: Sat Oct 26 12:05:26 PDT 1996 Andrew DeFaria +# Parameters: count (default 2): number of times to ping a machine before +# considering it not responding. +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) + +function usage { + print "$me: Usage: $me { count }" + print "\twhere count = number of times to ping a machine before +considering" + print "\tthe machine to be not responding (default count = 2)." + exit 1 +} # usage + +os= + +function get_os { + machine=$1 + # Attempt to determine the OS. First attempt to remsh to the machine and + # do a uname(1). This assumes Unix. We are unable to remsh then we'll + # assume that it's a PC (it could also be a line printer or any other of + # a number of network pingable devices!) + os=$(remsh $machine -n uname 2>&1) + + # We're gonna make some guesses here... + if [[ "$os" = "HP-UX" || + "$os" = "Linux" ]]; then + : Do nothing! + elif [[ "$os" = *Lost\ connection ]]; then + os="Linux? (Lost connection)" + elif [[ "$os" = *Permission\ denied. ]]; then + os="Linux? (Permission denied)" + elif [[ "$os" = *Login\ incorrect ]]; then + os="HP-UX? (Login incorrect)" + elif [[ "$os" = *Connection\ refused ]]; then + os="NT? (Connection refused)" + else + os="Unknown: $os" + fi +} # get_os + +integer count=2 + +if [ $# -eq 1 ]; then + count=$1 +elif [ $# -gt 1 ]; then + usage +fi + +esc=$(print "\033") + +if [ "$TERM" = "dtterm" -o -z DTTERM ]; then + export normal="$esc[39m" + export red="$esc[31m" + export green="$esc[32m" +elif [ "$TERM" = "hp" -o "$TERM" = "hpterm" ]; then + export normal="$esc&d@$esc&v0S" + export red="$esc&v1S" + export green="$esc&v2S" +fi + +# Print heading +print " Machine\t\t IP\tState\t OS" +print - "-----------------------\t-------------\t-----\t-------" + +subnet=${subnet:-15.28} +integer starting_subnet_octect=${starting_subnet_octect:-96} +integer ending_subnet_octect=${ending_subnet_octect:-103} +integer subnet_octect=starting_subnet_octect +integer starting_octect=${starting_octect:-0} +integer ending_octect=${ending_octect:-255} +integer octect=$starting_octect + +while [ $subnet_octect -le $ending_subnet_octect ]; do + while [ $octect -le $ending_octect ]; do + ip=$subnet.$subnet_octect.$octect + machine=$ip + nslookup $ip 2>&1 | grep Non-exist > /dev/null 2>&1 + if [ $? -ne 0 ]; then + machine=$(nslookup $ip | grep Name: | awk '{print $2}' | cut -f1 -d.) + fi + print "$machine\t\c" + if [ ${#machine} -lt 8 ]; then + print "\t\t\c" + elif [ ${#machine} -lt 16 ]; then + print "\t\c" + fi + print "$ip\t\c" + ping_result=$(ping $ip -n $count | tail -1 | grep "100% packet loss") + if [ -z "$ping_result" -eq 0 ]; then + print "${green}UP${normal}\t\c" + get_os $machine + print "$os" + else + print "${red}DOWN${normal}\tUnknown" + fi + let octect=octect+1 + done + let subnet_octect=subnet_octect+1 + let octect=starting_octect +done diff --git a/clients/HP/bin/reinstall_patches_bundle b/clients/HP/bin/reinstall_patches_bundle new file mode 100644 index 0000000..8f8241d --- /dev/null +++ b/clients/HP/bin/reinstall_patches_bundle @@ -0,0 +1,61 @@ +#!/bin/ksh +################################################################################ +# +# File: reinstall_patch_bundle +# RCS: $Header:$ +# Description: A script to reinstall the Patches bundle by removing it and +# reinstalling it. +# Author: Andrew DeFaria, California Language Labs +# Created: Thu May 1 23:06:48 PDT 1997 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=`basename $0` +notify=defaria@cup.hp.com +machine=`uname -n` +osver=`uname -r | cut -f2- -d.` +major_osver=`echo $osver | cut -f1 -d.` +depot="wampus:/Depots/$osver" + +if [ "$major_osver" = "10" ]; then + if [ `id -u` -ne 0 ]; then + print -u2 "$me: Error: You must be root to use this command" + exit 1 + fi + message="Reinstalling Patches bundle from $depot to $machine via $0" + mailx -s "$message" $notify < /tmp/$me.$$ 2>&1 + + if [ $? -ne 0 ]; then + message="Removal of Patches bundle failed!" + mailx -s "$message" $notify < /tmp/$me.$$ + exit 1 + fi + + # Next install the new Patches bundle. + /usr/sbin/swinstall \ + -x autoreboot=true \ + -s $depot Patches > /tmp/$me.$$ 2>&1 + + if [ $? -ne 0 ]; then + message="Installation of Patches bundle failed!" + mailx -s "$message" $notify < /tmp/$me.$$ + exit 1 + fi +else + message="Sorry but $0 only works on 10.x machines. This machine is +$major_osver." + exit 1 +fi diff --git a/clients/HP/bin/reinstall_unixsysadm b/clients/HP/bin/reinstall_unixsysadm new file mode 100644 index 0000000..058ab11 --- /dev/null +++ b/clients/HP/bin/reinstall_unixsysadm @@ -0,0 +1,80 @@ +#!/bin/ksh +################################################################################ +# +# File: reinstall_unixsysadm +# Description: Script to reinstall this package. +# Author: Andrew@DeFaria.com +# Created: Fri Sep 24 10:11:56 PDT 1999 +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +function usage { + if [ "_$1" != "_" ]; then + display "$1" + display + fi + display "Usage: $me" + exit 1 +} # usage + +# Check for execution by root +if is_not_root; then + error "This script must be run as root" 1 +fi + +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -v|-verbose) + verbose=yes + ;; + + -d|-debug) + debug=yes + ;; + + *) + usage "Unrecognized parameter $1" + ;; + esac + shift +done + +# Currently we must swremove and swinstall this package. In the future the +# swinstall package may be made smarter and clean up older files... + +verbose "Removing old UnixSysadm package..." +/usr/sbin/swremove UnixSysadm > /tmp/swremove.log 2>&1 + +if [ $? -ne 0 ]; then + error "Unable to swremove UnixSysadm! Check /tmp/swremove.log" +else + rm /tmp/swremove.log +fi + +verbose "Reinstalling new UnixSysAdm package..." +/usr/sbin/swinstall -s pdlapp:/var/depot/adm UnixSysadm > /tmp/swinstall.log +2>&1 + +if [ $? -ne 0 ]; then + error "Unable to swinstall UnixSysadm! Check /tmp/swinstall.log" +else + rm /tmp/swinstall.log +fi diff --git a/clients/HP/bin/remount_viewserver b/clients/HP/bin/remount_viewserver new file mode 100644 index 0000000..9338ba9 --- /dev/null +++ b/clients/HP/bin/remount_viewserver @@ -0,0 +1,48 @@ +#!/bin/ksh +################################################################################ +# +# File: remount_viewserver +# Description: This script will remount a viewservers LVMs. This is handy +# when a viewserver goes down and needs to be remounted to +# the build servers. +# Author: Andrew DeFaria (defaria@cup.hp.com) +# Language: Korn Shell +# Modified: +# +# (c) Copyright 1998, Hewlett-Packard Company, all rights reserved. +# + +################################################################################ +me=$(basename $0) +if [ $(id -u) -ne 0 ]; then + print -u2 "$me: Error: You must be root to execute this command!" + exit 1 +fi + +if [ $# -ne 1 ]; then + print -u2 "Usage: $me viewserver" + exit 1 +fi + +viewserver=$1 + +function remount { + cd /tmp_mnt/net/$viewserver/CLO/Storage/Views + for dir in *; do + print Mounting $viewserver:/CLO/Storage/Views/$dir to $PWD/$dir + /usr/sbin/mount $viewserver:/CLO/Storage/Views/$dir $PWD/$dir 2>&1 | +grep -v "already mounted" + done + cd $OLDPWD +} # remount + +function kick_automounter { + automount_pid=$(/app/sj automount | awk '{print $2}') + + print Kicking automounter \($automount_pid\) + kill -HUP $automount_pid +} # kick_automounter + +remount $viewserver + +kick_automounter diff --git a/clients/HP/bin/restart_system b/clients/HP/bin/restart_system new file mode 100644 index 0000000..855c7fc --- /dev/null +++ b/clients/HP/bin/restart_system @@ -0,0 +1,45 @@ +#!/bin/ksh +################################################################################ +# +# File: restart_system +# Description: A script to restart a system. Used for at jobs mainly. +# Author: Andrew@DeFaria.com +# Created: Wed Apr 16 14:14:17 PDT 1997 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +# Who do notify about the restart +notify=${notify:-$LOGNAME} + +if is_not_root; then + error "This script must be run as root" 1 +fi + +message="Restarting $(hostname) ($(uname -s)) via $me" +info $message +#mailx -s "$message" $notify < User $user is \"special\"" + print "$user:$pass:$uid:$gid:$geos:$home:$shell" + ;; + + *) + # In mail mode change shell to /bin/false. Otherwise use a restricted + # shell and also set the home directory to /home/vumover. + if [ ! -z "$mailmode" ]; then + shell=/bin/false + else + shell=/usr/bin/rksh + home=/home/vumover + fi + + # Change MoA marking to Restricted + geos="$(print "$geos" | sed 's/_MoA_/_Restricted_/')" + + # Allow no other uid 0 users other than those listed above + if [ $uid -eq 0 ]; then + verbose "****> User $user is in uid 0 - skipping..." + continue + fi + + # Skip users from MLL + print $home | grep "\.ch\.apollo" > /dev/null 2>&1 + if [ $? -eq 0 ]; then + verbose "****> User $user is from Apollo - skipping..." + continue + fi + + print "$user:$pass:$uid:$gid:$geos:$home:$shell" + ;; + esac +done < /etc/passwd > /tmp/passwd.$$ + +if [ "_$outfile" = "_" ]; then + mv /etc/passwd /etc/passwd.old + mv /tmp/passwd.$$ /etc/passwd + chmod 444 /etc/passwd +else + mv /tmp/passwd.$$ $outfile +fi diff --git a/clients/HP/bin/roll_logs b/clients/HP/bin/roll_logs new file mode 100644 index 0000000..b38dc79 --- /dev/null +++ b/clients/HP/bin/roll_logs @@ -0,0 +1,115 @@ +#!/bin/ksh +################################################################################ +# +# File: roll_logs +# Description: Rolls log files +# Author: Andrew@DeFaria.com +# Created: Thu Dec 9 10:05:09 PST 1999 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Set me to command name +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common +. $adm_fpath/logs + +function usage { + if [ "_$1" != "_" ]; then + display "$1" + display + fi + display "Usage: $me -[da|aily] | -[w|eekly]" + exit 1 +} # usage + +# Check for execution by root +if is_not_root; then + error "This script must be run as root" 1 +fi + +type= +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -v|-verbose) + verbose=yes + ;; + + -d|-debug) + debug=yes + ;; + + -da|-daily) + type=daily + ;; + + -w|-weekly) + type=weekly + ;; + + *) + usage "Unrecognized parameter $1" + ;; + esac + shift +done + +if [ "_$type" = "_" ]; then + usage "Must specify -daily or -weekly" +fi + +#Directory logfile backuplog what +dailylogs="\ +/var/adm automount.log autolog.week automount\n\ +/var/adm nettl.LOG00 nettl.week nettracelog\n\ +/var/adm ninstall.log nlog.week ninstall\n\ +/var/adm ptydaemonlog ptylog.week ptydaemon\n\ +/var/adm rpc.lockd.log rpc.lockd.week rpc.lockd\n\ +/var/adm rpc.statd.log rpc.statd.week rpc.statd\n\ +/var/adm vtdaemonlog vtlog.week vtdaemon\n\ +/var/adm/cron log log.week cron\n\ +/var/adm/lp log log.week lp\n\ +/var/adm/syslog syslog.log syslog.week syslogd" + +weeklylogs="\ +/var/adm autolog.week autolog.oldweek automount\n\ +/var/adm nettl.week nettl.oldweek nettracelog\n\ +/var/adm nlog.week nlog.oldweek ninstall\n\ +/var/adm ptylog.week ptylog.oldweek ptydaemon\n\ +/var/adm rpc.lockd.week rpc.lockd.oldweek rpc.lockd\n\ +/var/adm rpc.statd.week rpc.statd.oldweek rpc.statd\n\ +/var/adm vtlog.week vtlog.oldweek vtdaemon\n\ +/var/adm/cron log.week log.oldweek cron\n\ +/var/adm/lp log.week log.oldweek lp\n\ +/var/adm/syslog syslog.week syslog.oldweek syslogd" + +if [ "$type" = "daily" ]; then + verbose "Daily roll_logs" + logfiles="$dailylogs" +else + verbose "Weekly roll_logs" + logfiles="$weeklylogs" +fi + +print "$logfiles" | while read dir logfile backup_logfile what; do + verbose "Rolling ($what) logfile $logfile -> $backup_logfile..." + if [ "$type" = "weekly" ]; then + # Clear out oldweek file first + rm -f $backup_logfile + fi + roll_log $dir $logfile $backup_logfile $what +done diff --git a/clients/HP/bin/root b/clients/HP/bin/root new file mode 100644 index 0000000..e733b48 --- /dev/null +++ b/clients/HP/bin/root @@ -0,0 +1,43 @@ +#!/bin/bash +################################################################################ +# +# File: root +# Description: A script to go into "wizard" mode +# Author: Andrew@DeFaria.com +# Created: Mon May 17 07:35:59 PDT 1999 +# Language: Bash shell +# +# (c) Copyright 1999, Andrew DeFaria, all rights reserved. +# +################################################################################ +me=$(basename $0) + +# Set adm_base +adm_base=${adm_base:-$HOME/adm} + +# Set adm_fpath +adm_fpath=${adm_fpath:-$adm_base/functions} + +# Source functions +. $adm_fpath/common + +if [ ! -x $(type -p sudo) ]; then + warning "$me: Warning: Unable to find sudo!" + exit 1 +fi + +if [ $# -gt 0 ]; then + # Execute the commands + sudo $@ +else + # Become a "wizard"! + sudo -s + + if [ -x ~/.rc/functions ]; then + # Source in ksh functions (needed for set_title and set_prompt) + . ~/.rc/functions + # Reset title and prompt (if you can) + set_title + set_prompt + fi +fi diff --git a/clients/HP/bin/rootmail b/clients/HP/bin/rootmail new file mode 100644 index 0000000..2681dab --- /dev/null +++ b/clients/HP/bin/rootmail @@ -0,0 +1,49 @@ +#!/bin/ksh +################################################################################ +# +# File: rootmail +# RCS: $Header: rootmail,v 1.1 97/04/21 14:27:10 defaria Exp $ +# Description: Lists who receives root mail for a particular machine +# Author: Andrew DeFaria, California Language Labs +# Created: Mon Nov 13 16:14:30 1995 +# Modified: Mon Nov 13 16:16:56 1995 (Andrew DeFaria) defaria@spock +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +if [ -x /app/appserver ]; then + . /app/appserver +fi + +me=$(basename $0) + +function usage { + print -u2 "Usage: $me: hostname" + exit 1 +} # usage + +# Get parameters +if [ $# -eq 0 ]; then + host= +elif [ $# -eq 1 ]; then + host="/nfs/$1" +else + usage +fi + +path=usr/lib +if [ "_$host" = "_" ]; then + if [ "$OS" = "10" ]; then + path=etc/mail + fi +fi + +alias_file=$host/$path/aliases.local + +if [ ! -f $alias_file ]; then + print -u2 "$me: Unable to find local alias file: $alias_file" + exit 1 +fi + +grep "^root" $alias_file diff --git a/clients/HP/bin/rs b/clients/HP/bin/rs new file mode 100644 index 0000000..e912d96 --- /dev/null +++ b/clients/HP/bin/rs @@ -0,0 +1 @@ +/usr/bin/X11/resize diff --git a/clients/HP/bin/security b/clients/HP/bin/security new file mode 100644 index 0000000..2a28cea --- /dev/null +++ b/clients/HP/bin/security @@ -0,0 +1,157 @@ +#!/bin/ksh +################################################################################ +# +# File: audit +# Description: Security audit +# Author: Andrew DeFaria (defaria@cup.hp.com) +# Language: Korn Shell +# Modifications:Combined security checking scripts from Chip Chapin +# (chip@cup.hp.com) and Michael Coulter (coulter@cup.hp.com). +# +# (c) Copyright 1991, Hewlett-Packard Company, all rights reserved. +# +################################################################################ +# First source the appserver script +if [ -x /app/appserver ]; then + . /app/appserver +fi + +me=$(basename $0) + +case $OS in + 09) + crondir=/usr/spool/cron/crontabs + atdir=/usr/spool/cron/atjobs + ;; + 10) + crondir=/var/spool/cron/crontabs + atdir=/var/spool/cron/atjobs + ;; + *) + print -u2 "$me: Error: Unknown OS version: $OS" + exit 1 + ;; +esac + +function local_user { + # Determines is $user has a home directory local to this machine + first_component=$(print $home | cut -f2 -d/) + machine_component=$(print $home | cut -f3 -d/) + this_machine=$(uname -n) + + if [ "$first_component" = "nfs" -o "$first_component" = "net" ]; then + if [ $machine_component = $this_machine ]; then + return 1 + else + return 0 + fi + else + return 1 + fi +} # local_user + +function starred_out_checks { + print "$me: Warning: Non standard user \"$user\" has \"*\" out password!\n" + print "If this user no longer works here you should assign ownership of their" + print "files to somebody else then have this user's password entry removed.\n" + # If the password is "*", there should not be a .rhosts or hosts.equiv + # in the home directory or .forward + if [ -d "$home" ]; then + if [ -f "$home/.rhosts" ]; then + print "$me: Warning: User: $user has a .rhosts file in $home\n" + print "You should remove this user's ~/.rhosts file.\n" + fi + + if [ -f "$home/.forward" ]; then + print "$me: Warning: User: $user has a .forward file in $home\n" + print "You should remove this user's ~/.forward file.\n" + fi + fi # home directory exists + + # There should not be a crontab or atjob for the user + if [ -f $crondir/$user ]; then + print "$me: Warning: User: $user has a crontab file in $crondir/$user\n" + print "You should remove this user's crontab file.\n" + fi + + if [ -f $atdir/$user ]; then + print "$me: Warning: User: $user has a at file in $atdir/$user\n" + print "You should remove this user's at file.\n" + fi +} # starred_out_checks + +function check_users { + # This function checks users in the password file. + + # Parse all the lines in /etc/passwd + IFS=":" + while read user password uid gid comment home shell rest; do + # Check if the user has a local home directory + local_user $user $home + local=$? + + # Checks for users who shouldn't log-in, i.e. password is "*" + if [ $local -eq 1 ]; then # Only check local users + if [ "$password" = '*' ]; then + if [ "$user" = "adm" -o \ + "$user" = "anon" -o \ + "$user" = "bin" -o \ + "$user" = "daemon" -o \ + "$user" = "ftp" -o \ + "$user" = "lp" -o \ + "$user" = "nuucp" -o \ + "$user" = "rje" -o \ + "$user" = "root" -o \ + "$user" = "sync" -o \ + "$user" = "tftp" -o \ + "$user" = "uucp" -o \ + "$user" = "who" ]; then + : # Skip some users who should be starred out + else + starred_out_checks + fi + else + if [ "$password" = "" ]; then + print "$me: Warning: User: $user has a NULL password\n" + print "You must assign a proper password to this user.\n" + fi + + # No wildcards in ~/.rhosts or /etc/host.equiv + if [ -f ~/.rhosts -a $local -eq 1 ]; then + LINES="$(sed -e '/^#/d' ~/.rhosts | grep -e '+' 2> /dev/null | wc -l)" + if [ "$LINES" -ne 0 ]; then + print "$me: Warning: User: $user has \"+\" in $home/.rhosts\n" + print "This can be fixed by logging on as $user and running:" + print "/app/admin/bin/fixrhosts\n" + fi + fi + fi + fi + done < /etc/passwd +} # check_users + +function miscellaneous_checks { + # Check for execution by root + + if [ "$(whoami)" != "root" ]; then + print -u2 "$me: Error: This script must be run by root". + exit 1 + fi + + # Checks that are only done once + + # Check no wildcards in /etc/host.equiv + if [ -f /etc/hosts.equiv ]; then + lines="$(sed '/^#/d' /etc/hosts.equiv | grep -e '+' 2> /dev/null | wc -l)" + if [ "$lines" -ne 0 ]; then + print "$me: Warning: System has \"+\" in /etc/host.equiv\n" + print "You should remove this \"+\" from /etc/host.equiv\n" + fi + fi +} # miscellaneous_checks + +miscellaneous_checks +check_users +/usr/local/etc/admdaemon.dy -clear -secu -mailto root + +exit 0 diff --git a/clients/HP/bin/setup b/clients/HP/bin/setup new file mode 100644 index 0000000..56fb75c --- /dev/null +++ b/clients/HP/bin/setup @@ -0,0 +1,75 @@ +#!/bin/ksh +################################################################################ +# +# File: setup +# RCS: $Header: setup,v 1.2 99/02/15 20:35:58 root Exp $ +# Description: This script will setup the necessary links for the AppServer. +# Author: Andrew DeFaria, California Language Labs +# Created: Thu May 16 09:51:15 PDT 1996 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# First determine where/how the appserver is mounted on local machine +if [ -d "/net/bismol" ]; then + MOUNTPOINT=/net + export APPSERVER=bismol +elif [ -d "/net/hpclbis" ]; then + MOUNTPOINT=/net + export APPSERVER=hpclbis +elif [ -d "/nfs/bismol" ]; then + MOUNTPOINT=/nfs + export APPSERVER=bismol +elif [ -d "/nfs/hpclbis" ]; then + MOUNTPOINT=/nfs + export APPSERVER=hpclbis +fi + +# Export APPROOT properly +export APPROOT=$MOUNTPOINT/$APPSERVER + +function make_symlink { + if [ ! -h "$1" ]; then + ln -sf $APPROOT$1 $1 + fi +} # make_symlink + +make_symlink /etc/socks.conf +make_symlink /usr/lib/font +make_symlink /usr/local/lib/emacs +make_symlink /usr/local/etc/newsdomain +make_symlink /usr/local/hindsight_4.0 + +# Create ispell directory if not there already +mkdir -p /usr/local/lib/ispell + +make_symlink /usr/local/lib/ispell/english.hash + +# Create import directory if not there already. +mkdir -p /usr/vue/config/import + +# Special symlink +ln -sf $APPROOT/usr/vue/config/export/tools /usr/vue/config/import/tools +ln -s /app/mh /usr/local/bin/mh +ln -s /app/mh-lib /usr/local/lib/mh + +# Xsession stuff + +# Check that Xsession is indeed a link +if [ -f /usr/vue/config/Xsession ]; then + if [ ! -h /usr/vue/config/Xsession ]; then + mv /usr/vue/config/Xsession /usr/vue/config/Xsession.old + ln -sf $APPROOT/usr/vue/config/export/Xsession /usr/vue/config/Xsession + fi +fi + +# Turned off as per Mark Keil's request +#if [ ! -h /usr/softbench ]; then +# ln -sf $APPROOT/aspirin/softbench /usr/softbench +#fi + +# Fix ups for SURF +mkdir -p /opt/surf +make_symlink /opt/surf/newconfig diff --git a/clients/HP/bin/setup.new.system2 b/clients/HP/bin/setup.new.system2 new file mode 100644 index 0000000..7e08b81 --- /dev/null +++ b/clients/HP/bin/setup.new.system2 @@ -0,0 +1,195 @@ +#!/bin/ksh +################################################################################ +# +# File: setup.new.system2 +# RCS: $Header:$ +# Description: This script sets up a new system. +# Author: Andrew DeFaria, California Language Labs +# Created: Tue Apr 15 14:20:02 PDT 1997 +# Modified: +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Logfile +logfile=/tmp/setup.new.system2.log + +# Redirect all output to setup.new.system.log +exec | tee -a $logfile > /dev/tty 2>&1 + +## Set global env variables +# Set me +me=${0##*/} + +# Set OS +OS=$(uname -r | cut -c3-) + +# Set step_nbr +integer step_nbr=0 + +function error { + print -u2 "$me: Error: $1" +} # error + +function warning { + print -u2 "$me: Warning: $1" +} # warning + +function display { + print "$1" +} # display + +function info { + display "$me: Info: $1" +} # info + +function verbose { + if [ ! -z "$verbose" ]; then + display "$1" + fi +} # verbose + +function debug { + if [ ! -z "$debug" ]; then + print -u2 "$1" + fi +} # debug + +function usage { + display "$ME -c/learcase [-v|verbose] [-d|debug] [-usage]" + display " -c/learcase Perform ClearCase installation" + display " -v|verbose: Turns on verbose mode" + display " -d|debug: Turns on debug mode" + display " -usage: Print this usage message" + + error "$1" + exit 1 +} # usage + +function step { + let step_nbr=step_nbr+1 + display "Step #$step_nbr: $@" +} # step + +function install_clearcase { + license_host=wampus + registry_host=wampus + registry_region=cll + log_file=/tmp/ClearCase.install_log + os=$(print "$OS" | cut -c1-2) + + if [ "$os" = "09" ]; then + os="9" + fi + + case "$(uname -m)" in + 9000/712|9000/715) + model=link + ;; + 9000/829|9000/849) + model=full + ;; + + *) + model=standard + ;; + esac + + case "$clearcase" in + 2.1) + +clearcase_release_area=/net/bismol/aspirin/cc_v$clearcase/clearcase_v$clearcase/hp${os}_pa + cd $clearcase_release_area/install + ./install_release \ + -model $model \ + -to /usr/atria \ + -from $clearcase_release_area \ + -lh $license_host \ + -rh $registry_host \ + -rr $registry_region \ + -mvfs \ + -log $log_file \ + -local \ + -no_query >> $logfile + ;; + + 3.0) + + +clearcase_release_area=/net/wampus/opt/ccase_rls/clearcase_v$clearcase/hp${os}_pa + cd $clearcase_release_area/install + ./install_release \ + -model $model \ + -to /usr/atria \ + -from $clearcase_release_area \ + -lh $license_host \ + -rh $registry_host \ + -rr $registry_region \ + -mvfs \ + -log $log_file \ + -local \ + -comp +atria_install,atria_base,CC_base,atria_X11_base,atria_hlp_viewer,atria_server,CC_client,atria_cplus_base,atria_gui,CC_doc,CC_vob_svr,CC_bld_client,CC_view_svr,CC_int_client,CC_gui_client,CC_cnv_client,CC_MIN_STD,CC_ONLY_SERVER,CC_FULL +\ + -nlog \ + -level 5 \ + -no_query >> $logfile + ;; + + *) + usage "Unknown ClearCase version $clearcase" + ;; + esac +} # install_clearcase + +# Set initial parm values +clearcase= +verbose= +debug= + +# Get parameters +while [ $# -ge 1 ]; do + case "$1" in + -usage) + usage + ;; + + -v|-verbose) + verbose=yes + ;; + + -d|-debug) + debug=yes + ;; + + -c|-clearcase) + if [ $# -le 1 ]; then + usage "ClearCase version not specified!" + fi + shift + clearcase="$1" + ;; + + *) + usage "Unrecognized parameter $1" + ;; + esac + shift +done + +if [ $(id -u) -ne 0 ]; then + error "Must be root to execute this command" + exit 1 +fi + +if [ "_$clearcase" = "_" ]; then + usage "ClearCase version not specified!" +else + step "Installing ClearCase Version $clearcase" + display "This step will reboot the machine" + install_clearcase + # Preceeding step should reboot the system. If we get here then there + # is something definitely wrong! + error "Unable to install ClearCase Version $clearcase!" +fi diff --git a/clients/HP/bin/setup_cron b/clients/HP/bin/setup_cron new file mode 100644 index 0000000..924b59c --- /dev/null +++ b/clients/HP/bin/setup_cron @@ -0,0 +1,28 @@ +#!/bin/bash +################################################################################ +# +# File: setup_cron +# Description: This script sets up Cygwin's cron on the local machine +# Author: Andrew@DeFaria.com +# Created: +# Language: Bash Shell +# Modifications: +# +# (c) Copyright 2002, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) +# Make sure that certain directories and files do not exist! This is to let +# cron create them, which appears to be the only way to get these created +# correctly! +if [ -d /var/cron ]; then + rm -rf /var/cron + rm -rf /var/run/cron.pid + rm -rf /var/log/cron.log + + # Install cron service: + cygrunsrv -I cron -p /usr/sbin/cron -a -D -d "Cygwin cron" -e "MAILTO=$USER@broadcom.com" -e "CYGWIN=ntsec" +fi + +# Start cron service +cygrunsrv -S cron diff --git a/clients/HP/bin/setup_ssmtp b/clients/HP/bin/setup_ssmtp new file mode 100644 index 0000000..b1ea7d2 --- /dev/null +++ b/clients/HP/bin/setup_ssmtp @@ -0,0 +1,58 @@ +#!/bin/bash +################################################################################ +# +# File: setup_ssmtp +# Description: This script sets up ssmtp mail configuration +# Author: Andrew@DeFaria.com +# Created: Wed Jan 9 12:57:13 2002 +# Language: Bash Shell +# Modifications: +# +# (c) Copyright 2002, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +# Setup /etc/ssmtp config directory +ssmtp_dir=/etc/ssmtp +domain=${domain:-broadcom.com} +mail_server=smtphost + +mkdir -p $ssmtp_dir +chmod 700 $ssmtp_dir + +# Make some simple aliases. Alias $USER to the proper email address and then +# alias root, Administrator and postmaster to the user's address thus making +# the user "god" of smtp on this machine only. +cat > $ssmtp_dir/revaliases < $ssmtp_dir/ssmtp.conf <" + exit 1 +} # usage + +if [ $(id -u) -ne 0 ]; then + print -u2 "$me: Error: Must be root to execute this command!" + usage +fi + +# Get parameters +if [ $# -ne 1 ]; then + usage; +else + new_license_server="$1" +fi + +license_server_file=/usr/adm/atria/config/license_host + +if [ -f $license_server_file ]; then + old_license_server=$(cat $license_server_file) + if [ "$old_license_server" = "$new_license_server" ]; then + print -u2 "$me: The license server is already $new_license_server" + print -u2 "$me: Nothing changed!" + exit + fi + cp $license_server_file $license_server_file.old + print $new_license_server > $license_server_file + print "$me: Switched license server from $old_license_server \c" + print "to $new_license_server." + print "$me: Saved old license server setting in $license_server_file.old" +fi diff --git a/clients/HP/bin/switch_region b/clients/HP/bin/switch_region new file mode 100644 index 0000000..cdfc435 --- /dev/null +++ b/clients/HP/bin/switch_region @@ -0,0 +1,48 @@ +#!/bin/ksh +################################################################################ +# +# File: switch_region +# Description: A script to switch the registry region from one region to +# another. +# Author: Andrew DeFaria, California Language Labs +# Created: Wed Jan 15 16:52:22 PST 1997 +# Modified: Wed Jan 15 16:52:22 PST 1997 (Andrew DeFaria) defaria@cup.hp.com +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) + +function usage { + print "Usage: $me: " + exit 1 +} # usage + +if [ $(id -u) -ne 0 ]; then + print -u2 "$me: Error: Must be root to execute this command!" + usage +fi + +# Get parameters +if [ $# -ne 1 ]; then + usage; +else + new_registry_region="$1" +fi + +registry_region_file=/usr/adm/atria/rgy/rgy_region.conf + +if [ -f $registry_region_file ]; then + old_registry_region=$(cat $registry_region_file) + if [ "$old_registry_region" = "$new_registry_region" ]; then + print -u2 "$me: The registry region is already $new_registry_region" + print -u2 "$me: Nothing changed!" + exit + fi + cp $registry_region_file $registry_region_file.old + print $new_registry_region > $registry_region_file + print "$me: Switched registry region from $old_registry_region \c" + print "to $new_registry_region." + print "$me: Saved old registry setting in $registry_region_file.old" +fi diff --git a/clients/HP/bin/switch_rgy b/clients/HP/bin/switch_rgy new file mode 100644 index 0000000..ded8c03 --- /dev/null +++ b/clients/HP/bin/switch_rgy @@ -0,0 +1,48 @@ +#!/bin/ksh +################################################################################ +# +# File: switch_rgy +# Description: A script to switch the registry host from one machine to +# another. +# Author: Andrew DeFaria, California Language Labs +# Created: Wed Jan 15 16:52:22 PST 1997 +# Modified: Wed Jan 15 16:52:22 PST 1997 (Andrew DeFaria) defaria@cup.hp.com +# Language: Korn Shell +# +# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved +# +################################################################################ +me=$(basename $0) + +function usage { + print "Usage: $me: " + exit 1 +} # usage + +if [ $(id -u) -ne 0 ]; then + print -u2 "$me: Error: Must be root to execute this command!" + usage +fi + +# Get parameters +if [ $# -ne 1 ]; then + usage; +else + new_registry_server="$1" +fi + +registry_host_file=/usr/adm/atria/rgy/rgy_hosts.conf + +if [ -f $registry_host_file ]; then + old_registry_server=$(cat $registry_host_file) + if [ "$old_registry_server" = "$new_registry_server" ]; then + print -u2 "$me: The registry server is already $new_registry_server" + print -u2 "$me: Nothing changed!" + exit + fi + cp $registry_host_file $registry_host_file.old + print $new_registry_server > $registry_host_file + print "$me: Switched registry server from $old_registry_server \c" + print "to $new_registry_server." + print "$me: Saved old registry setting in $registry_host_file.old" +fi diff --git a/clients/HP/bin/sysinfo b/clients/HP/bin/sysinfo new file mode 100644 index 0000000..03bc8f3 --- /dev/null +++ b/clients/HP/bin/sysinfo @@ -0,0 +1,5851 @@ +#!/bin/ksh +# +# +# Title: sysinfo +# Author: Scott Truesdale +# Organization: Hewlett-Packard Account Support Organization +# America's Engineering Services Team +# +# NOTE: This script is a contributed utility. No official support for it +# will be provided by Hewlett-Packard. Any requests for bug fixes +# or enhancements will be handled by the author, time permitting. +# +# Version: 1.00 08/01/96 +# Changes: 1.01 08/05/96 added functions to handle each module +# 1.02 \ cleaned up various +# 1.03 / output screens +# 1.04 08/20/96 added filesystem check using lvol +# 1.05 08/25/96 added savecore flag and space check +# 1.06 09/03/96 changed filesystem check to use mnttab +# 1.07 09/03/96 fixed filesystem check when bdf is 2 lines +# 1.08 10/10/96 general cleanup, added more dump checking +# 1.09 10/10/96 added use of pager variable for output +# 1.10 10/23/96 fixed dump checking section (I hope) +# added 10.10 compression calculation (-s) +# 1.11 10/30/96 fixed display of number of stale extents (-l) +# added comments to help document functions. +# 1.12 10/31/96 added -N option to disable paging option. +# 1.13 11/06/96 added cpu speed display (in MHz). (-s) +# 1.14 11/08/96 removed tabs and added comments. +# 1.15 11/11/96 added check for non HP-UX systems. +# 1.16 11/20/96 expanded mapping to include number of +# extents and mirrors. (-m) +# 1.17 11/21/96 added kernel size display (-k) +# added check for non-LVM disks (-p) +# 1.18 11/26/96 added license, machine id (-s) +# added domain name (-k) +# fixed batch to include header information +# added check for vgcfg files > 1 year old. (-v) +# added kernel size to memory dump calculations +# 1.19 12/03/96 fixed error if no CD mounted in CD-ROM drive +# changed help (-h) to use man page +# 1.20 12/12/96 added physical disk mapping option (-M) +# for 10.x systems +# added dbc_max_pct kernel parm +# 1.21 12/17/96 added physical disk mapping for 9.x +# 1.22 01/07/97 fixed bug in path display for cdroms +# 1.23 02/04/97 added last patch date display +# 1.24 02/10/97 fixed bug in physical check for cdrom +# device busy error +# 1.25 02/12/97 fixed display of dump on default for 9.x +# 1.26 02/28/97 added display of default router +# 1.27 03/10/97 added display of MAC address +# 1.28 03/18/97 added display of alternate links +# added bootable check to Physical info +# 1.29 04/15/97 added check for dynamic buffer caching +# added nflocks kernel parm +# added check for async disk write +# added check for savecore compression in +# /etc/rc.config.d/savecore +# fixed exit code for warnings +# added auto boot string output +# 1.30 04/22/97 cleaned up swap and dump disk display +# added check for cdfs to % full warning +# fixed dbc and its warning for only 10.x +# added ability to specify savecore +# compression factor as variable +# 1.31 04/22/97 fixed bug in calculating savecore capacity +# 1.32 04/22/97 added display of boot disks +# 1.33 07/08/97 fixed pre 10.20 warning for not +# enough savecore space +# 1.34 07/19/97 added dns server display +# changed netmask to decimal.dot format +# 1.35 07/29/97 added use of model command to detect new +# system models +# added following from Mike Ryan: +# fixed display of multiple MAC addresses +# patch for pvdisplay difference in 10.20 +# fix VG display when PVG-strict policies used +# (thanks Mike!) +# 1.36 07/31/97 fixed bug in alternate PV link detection +# 1.37 08/01/97 added super page check +# removed dynamic buffer warning +# improved debug message handling +# changed debug messages to tee to file +# 1.38 08/17/97 added check for number of volume groups equal +# to maxvgs +# started adding HTML hooks (-H option) +# added HTML headers to each section +# added HTML links to each section +# added HTML info to man page +# 1.39 08/20/97 added check for existing logfiles +# ask user if we should delete batch logfile +# automatically delete HTML logfile +# changed temp file names to sysinfo.$$. +# 1.40 08/26/97 changed physical disk section to not query +# alternate links +# 1.41 08/28/97 added kernel parms section extracted using +# tools from SAM. +# 1.42 09/19/97 changed network to use lanscan due to +# problems with multiple I/F cards. +# 1.43 10/21/97 added hw path to network card info. +# added fstype display to filesystem info. +# fixed display of cdfs space usage. +# fixed man page that incorrectly stated free +# space threshold was 10% instead of 5%. +# 1.44 04/09/98 major enhancements +# added ioscan listing. +# added software listing (10.x only) +# added listing of striped discs. +# launched io, kernel and lvm scans +# as background jobs to speed things up. +# 1.45 04/17/98 fixed bug that caused dbc parms not to +# be displayed. +# 1.46 04/30/98 changed swap disk header to swap data +# added fs swaptype to total swap display +# 1.47 05/03/98 added warning about static buffer cache +# numbers reported from kernel. +# 1.48 05/20/98 added variable for logfile directory. +# removed user query if removing existing logfile. +# reworked logical volume section into two parts. +# 1.49 07/01/98 first set of changes to work on 11.x +# savecore now called savecrash +# changed BOOTABLE to check for >= 10 +# physmem now called phys_mem_pages +# 1.49a added host name to WARNING messages +# added TOP link to html output +# 1.49b 07/16/98 fixed problems for 9.x induced by background +# scans of sam, swap and ioscan +# samscan, ioscan, swapmem +# removed samscan (getkinfo) from 9.x +# changed to force getkinfo to rescan (-b) +# 1.49c 07/23/98 more ioscan changes for 9.x +# 1.49d 07/27/98 major kernel changes +# 1.49e 08/01/98 fixed problem with filesystem display +# if size greater than 65 GB. +# 1.49f 08/03/98 added separate 9.x kernel function +# including many more kernel metrics +# moved kernel size to system section +# rewrote header section for HTML +# added batch print for software section +# changed lanscan -i for 9.x +# 1.49g 08/05/98 lots +# 1.50 08/06/98 fixed kernel parms on 10.x & 11.x +# 1.51 08/27/98 fixed savecore free space calculation when +# using non-standard lvol name +# added cstm extract of cpu & memory info +# 1.52 10/14/98 fixed adb on 11.0 with 64 bit kernel +# added display of 32 or 64 bit kernel on 11.0 +# 1.53 10/16/98 added processor info from stm +# removed old SAM kernel code +# 1.54 10/20/98 increased field for PDC to 12 chars +# 1.55 10/20/98 fixed ifconfig problem with grep +# 1.56 10/20/98 added No such file or directory check to diskinfo +# changed rphysvol to use ioscan rather than +# calculating +# increased display of hwpath for EMC drives +# 1.57 10/22/98 fixed bug in getting rdsk name +# 1.58 10/27/98 added bad block relocation to logical display +# added warning for EMC disks with LVM bad +# block relocation enabled +# 1.59 11/3/98 added check for iodevice w/o phyvol +# +# 1.70 04/12/99 added check for unsupported cstm versions +# < 09 & > 13 +# 1.71 04/12/99 Numerous changes by Greg Sterling +# Code handles CRASHCONF utility +# Allows for dump devices in V11.x. +# Dump devices can be disk drives in addition +# to logical volumes. +# Allow for Fibrechannel devices to be +# accurately parsed in the Phys Device section. +# Updated the wait for ioscan to complete. +# The prior time was 15 seconds, this is not +# enough for a large system with many devices. +# I increased the wait to 125 seconds. +# Fixed the "Last Patch" section to work with +# a standard swinstall type system. +# Added security check. Checks file permissions +# on some critical files and dirs, and checks +# modem access. +# Check system log files (i.e. syslog.log, +# mail.log, btmp, and wtmp). These files are +# known to get large very quickly. +# Changes the Date stamp on output files +# to include a four digit year for Y2K reasons. +# Check the sendmail queue for old (older than +# 2 days) or large qty of entries. +# Added code to check/output information +# related to system diags (if its installed). +# This included diags and predictive. +# This option only applies to V10.x and up. +# Fixed a few bugs in the code which limited +# output to 10.10 or 10.20 systems. I had +# to change some of the IF statements. +# For V10.x systems and higher I added the +# /var/adm/sw/*.log files to the logfile +# check routine. +# 1.72 04/25/99 fixed bug in crashconf size calculation +# added ip name to ip addr in networking +# moved 32/64 bit to follow OS version +# added check for non-configured software +# i.e. transient, corrupt, available +# or installed +# added listing of boot paths under bootable disks +# 1.73 05/14/99 fixed cstm hang problem - finally! +# vers 12 reversed the meanings of the +# SaveAs and Print commands for Infolog +# 1.74 05/25/99 improved formatting of cstm output +# 1.75 06/02/99 fixed bug when cstm_tot_phys = N/A +# added path to crashconf executable (/sbin/) +# fixed bug when Online dir has .2 suffix +# 1.76 07/12/99 several performance changes thanks to J. Semroc +# changed filesystem section +# added vxfs fragmentation; removed inodes +# added check if Vxfs version < 3 on > 10.10 +# replaced cstm memory output +# added umask of 077 for security purposes +# 1.77 08/03/99 fixed Ignite version check for older versions +# removed cd-rom size from total p_size +# 1.78 09/07/99 added PA RISC chip and version +# removed cstm upper version check +# 1.78a 9/14/99 removed DVD size from total p_size +# increased sum lines for CAPACITY +# increased display size of product (model) +# 1.78b 9/18/99 Incorporated numerous changes by Jerry Schwartz: +# changed HTML output to use frames +# #fixed bug in physical disk enumeration +# fixed bug in check for /.profile +# made cosmetic changes to error messages +# ("Logfile" changed to "Important file", +# "doesn not" changed to "does not") +# 1.78c 10/2/99 Added cstm disk scan to retrieve firmware +# and serial number info. +# 1.78d 10/11/99 Updated models table +# 1.78e +# 1.78f Added XP256 information +# 1.79b +# | +# 1.79f 04/18/00 Added INSTALL script +# Added checks for diagmond to prevent hangs +# Added disk array checks for FC60, AutoRaid +# Cascade, and Nike. +# 1.79g 04/19/00 Added return to diagmond check +# 1.79h 05/15/00 Added additional system models +# Removed get_array_data due to cstm problem +# +# +# Acknowledgements: +# I would like to acknowledge the following for assistance in +# developing and testing this tool. +# +# Jerry Schwartz, Jeff Semroc, Greg Sterling, Mike Ryan & Bill Taylor +# from the HP Account Support Organization for their help in debugging +# and improving the code. +# +# LVMcollect.* scripts from Peter Van Giel in the Hewlett-Packard +# Country Response Center Belgium. +# capture script from Dave Olker of the Hewlett-Packard Worldwide +# Technology Expert Center. +# +# Most of all I would like to thank the PRODUNIX support team +# at Oracle Corp. for allowing me access and time to develop +# and test using their systems. +# +# +version="1.79h" # +umask 077 +ROOT=$PWD # +export RESOLV_CONF=/etc/resolv.conf # +#script=`basename $0` # +script="SysInfo" # +typeset PAGER=${PAGER:-more} # +typeset -x osmajor # +typeset -R4 lcount=0 # number of logical volumes +typeset -R4 pcount=0 # number of physical volumes +typeset -R4 vgcount=0 # number of volume groups +typeset -i total_stale=0 # number of stales extents +typeset stale=0 # flag set if stale extents found +typeset all_logvols # list of all logical volumes +typeset -L17 physvol # name of phyical volume +typeset -R21 rphysvold # name of raw phyical volume +typeset -R24 hwpathd # path information for disk +typeset -L8 vendor # vendor id string from disk +typeset EMC_found=0 # flag for EMC disks +typeset -R14 product # vendor model number from disk +typeset -R1 bootable_pv # is disk bootable? +typeset -R5 rev_level # firmware revision code from disk +#typeset -R12 kernel_size #\ +typeset -i shmem # \ +typeset -R8 shmmni # \ +typeset -R8 shmmax # \ +typeset -R8 shmseg # \ +typeset -R8 maxfiles # \ +typeset -R8 maxfiles_lim # \ +typeset -R8 maxuprc # \ +typeset -R8 nproc # \ +typeset -R8 fs_async # \ +typeset -R8 nfile # kernel parameters +typeset -R8 nflocks # +typeset -R8 super_page_support # / +typeset -R8 dbc_max_pct # / +typeset -R8 dbc_min_pct # / +typeset -R8 ninode # / +typeset -R8 npty # / +typeset -R8 nbuf # / +typeset -R8 bufpages # / +typeset -R8 maxvgs # / +typeset SAVECORE # flag set if savecore is enabled +typeset COMPRESS=0 # +typeset COMPRESSION="N/A" # set if on 10.10 or later +typeset Compress_Option="N/A" # enabled in /etc/rc.config.d/savecore +typeset -R8 lv_pbuf_cnt # \ +typeset -R8 lv_pbuf_inuse # \ +typeset -R8 lv_pbuf_maxuse # more kernel parameters +typeset -R8 lv_pbuf_pending_Q # for lvm +typeset -R8 lv_vgs_opn # / +typeset -R8 lv_lvs_opn # / +typeset -i psize # physical disk size (in KBytes) +typeset -R8 psize_mb # physical disk size (in Mbytes) +### Changed 6/15/99 J.Semroc - Changed to handle large systems with 1000+ LU's +### typeset -R3 lu # physical disk logical unit number +typeset -R4 lu # physical disk logical unit number +### end of Change +typeset -L20 volgroup # +typeset -R6 CURLV # +typeset -R10 CURPV # +typeset -R8 PeSize # +typeset -R9 AllocPe # +typeset -R8 FreePe # +typeset -R8 TotalPe # +typeset -R11 AllocMb # +typeset -R8 FreeMb # +typeset -R8 TotalMb # +typeset -R8 TotalAllocMb=0 # total allocated Megabytes +typeset -R8 TotalFreeMb=0 # total free Megabytes +typeset -R8 SystemTotalMb=0 # total of all Megabytes +typeset -R8 MirrorMb=0 # size of mirror +typeset -R8 TotalMirrorMb=0 # total size of mirrors +typeset -R8 OtherMb=0 # +typeset -R11 total_p_mb=0 # total physical Megabytes +typeset -R8 total_l_mb=0 # total logical Megabytes +typeset -R8 unused_cap=0 # +typeset -L4 fs_type # +typeset -R4 vxfs_frag # +typeset -i no_lost=0 # flag if lost+found/.fsadm not found +typeset logvol # +typeset -L40 logvold # +typeset pvol # +typeset -L16 statusout # status of extents (e.g. stale) +typeset -R6 LVSize # size of logical volume +typeset -R7 LogicalExtents # +typeset -R8 StaleExtents # +typeset -L35 MOUNT # +typeset -R8 MirrorCopies # number of mirrors +typeset -R8 Consistency # mirror consistency strategy +typeset -i REAL_MEM # memory size (in pages) +typeset -i memory # memory size (in MBytes) +typeset -i num_cpus # number of active CPUs +typeset -i StaleMB=0 # stale Megabytes +typeset -i TotalStaleMb=0 # total stale Megabytes +typeset -i cpu_speed=0 # clock speed in MHz +typeset exit_code=0 # exit code [ 0, 1, 2, 3 ] +typeset ERROR=1 # exit condition (program error) +typeset WARN=2 # exit condition (system warning) +typeset SYS_ERROR=3 # exit condition (system error) +typeset SYSTEM=0 # \ +typeset FULL_KERNEL=0 # \ +typeset LITE_KERNEL=0 # \ +typeset FILESYSTEM=0 # \ +typeset NETWORK=0 # \ +typeset DEBUG=0 # \ +typeset SWLIST=0 # \ +typeset PHYSICAL=0 # flags set at runtime via +typeset LOGICAL=0 # command line options +typeset IOSCAN=0 # / +typeset -x HTML=0 # / +typeset NOPAGER=0 # / +typeset VOLUMES=0 # / +typeset CAPACITY=0 # / +typeset BATCH=0 # / +typeset LMAP=0 # / +typeset PMAP=0 #/ +typeset KernelSafetyFactor=2 # adds two MBytes to kernel size +typeset lvm_installed=0 # flag set if lvm present +typeset -R6 DumpDiskCapacity=0 # space to hold memory dump +typeset -R6 CompressedMemoryDumpSize # +typeset -R6 compression_factor # +typeset -i MemoryDumpSize # amount of memory (in MBytes) +typeset -i TotalMemoryDumpSize # amount of memory + kernel size +typeset -R6 MemoryDumpSizeOut # display version of MemoryDumpSize +typeset -R6 TotalMemoryDumpSizeOut # display version of TotalMemoryDumpSize +typeset -R6 SaveCoreAvail_Out # space to hold savecore + # can be compressed on 10.10 and later +############## +# Variables for Chk_Logfiles section +############## +LOGFILELISTV10="/var/adm/syslog/syslog.log /var/adm/syslog/mail.log \ + /var/adm/btmp /var/adm/wtmp /var/adm/sw/swagent.log /var/adm/sw/swagentd.log \ + /var/adm/sw/swconfig.log /var/adm/sw/swcopy.log /var/adm/sw/swinstall.log \ + /var/adm/sw/swmodify.log /var/adm/sw/swreg.log /var/adm/sw/swremove.log" + +LOGFILELISTV9="/usr/adm/syslog /usr/spool/mqueue/log /etc/btmp /etc/wtmp" +############## +# Variables for Chk_SysAccess section +############## +WORLDREADFILES="/etc/passwd /etc/group /etc/hosts /etc/services \ + /etc/inetd.conf" + +NOREADFILES=~root/.profile +############## +# variables for io_scan section +############## +GREP_ARGS="" +GREP_V_ARGS="-eNO_HW" +typeset -x ioscan_args="-F" +typeset -x io_scan_out=/tmp/sysinfo.$$.io_scan.out +typeset -x io_scan_tmp=/tmp/sysinfo.$$.io_scan.tmp +typeset -x io_scan_done=/tmp/sysinfo.$$.io_scan_done +typeset -x io_scan_command_file=/tmp/sysinfo.$$.io_scan_command_file +############## +# variables for lvm_scan section +############## +typeset -x vg_list_file=/tmp/sysinfo.$$.vg.list +typeset -x vg_out_file=/tmp/sysinfo.$$.vg.out +typeset -x lvol_list_file=/tmp/sysinfo.$$.lvol.list +typeset -x lvol_out_file=/tmp/sysinfo.$$.lvol.out +typeset -x pvol_list_file=/tmp/sysinfo.$$.pvol.list +typeset -x pvol_out_file=/tmp/sysinfo.$$.pvol.out +typeset -x lvm_scan_command_file=/tmp/sysinfo.$$.lvm_scan_command_file +typeset -x lvm_scan_done=/tmp/sysinfo.$$.lvm_scan_done +############## +# variables for sam_scan section +############## +typeset -x sam_scan_command_file=/tmp/sysinfo.$$.sam_scan_command_file +typeset -x sam_scan_done=/tmp/sysinfo.$$.sam_scan_done +typeset -x sam_out_file=/tmp/sysinfo.$$.sam.out +#typeset -x sam_kinfo=/tmp/sysinfo.$$.kinfo +typeset -x sam_kinfo_new=/tmp/sysinfo.$$.kinfo_new +############## +# variables for kernel section +############## +typeset -x sam_kinfo=${sam_kinfo:-/tmp/sysinfo.$$.sam_kinfo} +typeset -x all_parms_file=/tmp/sysinfo.$$.all_parms +typeset -x all_classes_file=/tmp/sysinfo.$$.all_classes +############## +# variables for cstm section +############## +typeset -x cstm_command_file=/tmp/sysinfo.$$.cstm_command_file +typeset -x cstm_cpu_out=/tmp/sysinfo.$$.cstm_cpu_out +typeset -x cstm_cpu_out2=/tmp/sysinfo.$$.cstm_cpu_out2 +typeset -x cstm_mem_out=/tmp/sysinfo.$$.cstm_mem_out +typeset -x cstm_disk_out=/tmp/sysinfo.$$.cstm_disk_out +typeset -x cstm_array_out=/tmp/sysinfo.$$.cstm_array_out +############# +# variables for sap section +############# +# translate uppercase to lowercase +TRUL='tr "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"' +typeset -x r3_dir=/var/opt/perf/datafiles/r3dsi +typeset -x r3_prog=${r3_dir}/r3agent +typeset -i num_instances=0 + + +ALL="TRUE" +FORCE="FALSE" +TEMPFILE=/var/sam/sam.$$ +save_lang=$LANG +LANG=C + + +################################################################################ +# Function: DebugOn() +# Description: Turns debugging mode on. +# Arguments: none +# Returns: none + +DebugOn() { +# set -o xtrace + export _DEBUG=1 + export _DEBUG_FILE=/tmp/sysinfo_${sysname}_`date '+%Y%m%d'`_debug + #Debug Debug ON: info will be logged to screen and to $_DEBUG_FILE + print "\nDebug ON: info will be logged to $_DEBUG_FILE" + Debug Debug ON: info will be logged to $_DEBUG_FILE +} + +################################################################################ +# Function: DebugOff() +# Description: Turns debugging mode off. +# Arguments: none +# Returns: none + +DebugOff() { + + Debug Debug OFF + _DEBUG=0 + _DEBUG_FILE=$ROOT/debug.out +} + +############################################################################### +# Function: Debug() +# Description: Prints debug statements to stderr when debug mode is on. +# Arguments: Text to echo. +# Returns: none + +Debug() { + + if [ "$_DEBUG" -gt 0 ] + then + #echo "DEBUG (PID $$): $*" | tee -a $_DEBUG_FILE 1>&2 + echo "DEBUG (PID $$): $*" >> $_DEBUG_FILE 2>&1 + fi +} + + +#-----------------------------------------------------------# +# Exit function, called by trapped signals +#-----------------------------------------------------------# +function _sigexit +{ +# print "Cleaning up, please wait." + if (($DEBUG)) && ((!(($NOPAGER)) )) + then + read answer?"Delete temp files? [Y/n]" + if [[ ${answer} = n ]] + then + print "O.K. Don't forget to delete /tmp/sysinfo.$$.*" + print "exit value = ${exit_code}" + else + print "O.K. I'm deleting temp files /tmp/sysinfo.$$.*" + rm -f /tmp/sysinfo.$$.* > /dev/null 2>&1 + print "exit_code = ${exit_code}" + fi + else + rm -f /tmp/sysinfo.$$.* > /dev/null 2>&1 + fi + exit ${exit_code} +} +trap _sigexit EXIT KILL HUP QUIT INT + + +function usage +{ +cat << EOF | $PAGER + +${script} ${version} by Scott Truesdale + +Usage: ${script} [-aADfikKlLmMnpsSv] [-b|-H|-N] + ${script} [-h] + + -a displays all of the following options. + -A displays file access security. + -D checks system diagnostic information. + -f checks filesystems for free space. + -h displays detailed help message. + -i displays io configuration. + -k displays brief kernel parameters. + -K displays verbose kernel parameters. + -l displays logical volume data. + -L displays system logfile info. + -m displays logical to physical disk mapping. + -M displays physical to logical disk mapping. + -n displays network interface data. + -p displays physical disk data. + -P checks for R/3 instances. + -s displays the system and root disk data. + -S displays software listing (swlist). + -v displays volume group data. + + -b send output to a logfile. + -H send output to a logfile in HTML format. + -N turn off paging in screen mode. + +Note: disk capacity data is displayed only when using -a or -lpv. + +EOF +} # end of usage + +function print_help +{ +export MANPATH=/usr/contrib/man:${where}:$MANPATH +# to debug help -d must come before -h +Debug "print_help" +Debug " where=${where}" +Debug " MANPATH=${MANPATH}" +if [[ -f ${where}/man1m/sysinfo.1m ]] || \ + [[ -f /usr/contrib/man/man1m/sysinfo.1m ]] +then + man sysinfo + exit_code=0 +else + print "Cannot find man page for ${script}!" + print "Expected to find it in /usr/contrib/man/man1m/" + exit_code=${ERROR} +fi +} + + +#=================================================================== +# get_args +# gets command line arguments passed in at run time +#=================================================================== +function get_args +{ +if (($# == 0)) +then +#print "\n$Must specify one or more of the following:$\n" + usage + exit ${ERROR} +fi +while getopts :abdfhiklmnpsvADHKLMNPS arguments +do + case $arguments in + a) LITE_KERNEL=1 + LOGFILES=1 + FILEACCESS=1 + DIAGNOSTICS=1 + SYSTEM=1 + NETWORK=1 + IOSCAN=1 + LVMSCAN=1 + PHYSICAL=1 + VOLUMES=1 + LOGICAL=1 + FILESYSTEM=1 + SWLIST=1 + PMAP=1 + LMAP=1;; + A) FILEACCESS=1;; + b) BATCH=1 + + LOGDIR=${SYSINFO_LOGDIR:-"/tmp"} + LOGFILE="${LOGDIR}/sysinfo_${sysname}_`date '+%Y%m%d'`" + Debug "Removing $LOGFILE" + rm -f $LOGFILE > /dev/null 2>&1 + + ;; + d) DEBUG=1 + DebugOn;; + D) DIAGNOSTICS=1;; + f) FILESYSTEM=1;; + h) print_help + exit ${exit_code};; + H) HTML=1 + BATCH=1 + LOGFILES=1 + LITE_KERNEL=1 + FILEACCESS=1 + DIAGNOSTICS=1 + SYSTEM=1 + NETWORK=1 + PHYSICAL=1 + VOLUMES=1 + IOSCAN=1 + LVMSCAN=1 + LOGICAL=1 + FILESYSTEM=1 + SWLIST=1 + PMAP=1 + LMAP=1 + LOGDIR=${SYSINFO_LOGDIR:-"/tmp"} + LOGFILE="${LOGDIR}/sysinfo_${sysname}_`date '+%Y%m%d'`.html" + if [[ -f ${LOGFILE} ]] + then + rm -f $LOGFILE >> /dev/null 2>&1 + Debug "Removing $LOGFILE" + fi + LOGFILE_INDEX_REL="`basename ${LOGFILE} .html`.index.html" + LOGFILE_INDEX="`dirname ${LOGFILE}`/${LOGFILE_INDEX_REL}" + if [[ -f ${LOGFILE_INDEX} ]] + then + rm -f ${LOGFILE_INDEX} >> /dev/null 2>&1 + Debug "Removing ${LOGFILE_INDEX}" + fi + LOGFILE_MAIN_REL="`basename ${LOGFILE} .html`.main.html" + LOGFILE_MAIN="`dirname ${LOGFILE}`/${LOGFILE_MAIN_REL}" + if [[ -f ${LOGFILE_MAIN} ]] + then + rm -f ${LOGFILE_MAIN} >> /dev/null 2>&1 + Debug "Removing ${LOGFILE_MAIN}" + fi;; + i) IOSCAN=1;; + k) LITE_KERNEL=1;; + K) FULL_KERNEL=1;; + l) LOGICAL=1 + LVMSCAN=1;; + L) LOGFILES=1;; + m) LMAP=1;; + M) PHYSICAL=1 + SYSTEM=1 + PMAP=1;; + n) NETWORK=1;; + p) PHYSICAL=1 + SYSTEM=1;; + P) PASS=1;; + s) SYSTEM=1;; + S) SWLIST=1;; + v) VOLUMES=1 + LVMSCAN=1;; + N) NOPAGER=1;; + ?) print "\nI don't understand that option\n" + usage + exit ${ERROR};; + esac + if (($PHYSICAL)) && (($VOLUMES)) && (($LOGICAL)) + then + CAPACITY=1 + fi + if (($LITE_KERNEL)) && (($FULL_KERNEL)) + then + LITE_KERNEL=0 + fi + if (($BATCH)) && (($DEBUG)) + then + NOPAGER=1 + fi +done +Debug "FLAGS: System=$SYSTEM Batch=$BATCH FullKernel=$FULL_KERNEL" +Debug "FLAGS: LiteKernel=$LITE_KERNEL Network=$NETWORK" +Debug "FLAGS: Volumes=$VOLUMES Capacity=$CAPACITY Logical=$LOGICAL" +Debug "FLAGS: Physical=$PHYSICAL LMapping=$LMAP PMapping=$PMAP" +Debug "FLAGS: Filesystem=$FILESYSTEM NoPager=$NOPAGER Html=$HTML" +Debug "FLAGS: Ioscan=$IOSCAN lvm_scan=$LVMSCAN Sam_scan=$SAMSCAN" +Debug "FLAGS: Swlist=$SWLIST Diagnostics=$DIAGNOSTICS" +Debug "FLAGS: PASS=$PASS" +if (($BATCH)) +then + print "\nOutput going to ${LOGFILE}" +fi +} # end of get_args + + +#=================================================================== +# f_display_file +# this function displays a file to the screen. it takes one parm, +# the file to display. It uses the defined PAGER or more if that +# variable is not defined. +#=================================================================== +function f_display_file +{ + if [[ -s $1 ]] + then + if (($BATCH)) + then + cat $1 >> $LOGFILE + if (($HTML)) + then + grep -v "#Top" $1 | grep -v "TOP> $LOGFILE_MAIN + fi + elif (($NOPAGER)) + then + cat $1 + else # not batch so use PAGER + $PAGER $1 + fi + fi +} # end of f_display_file + + +############################################################################### +# Function: check_for_sap +# Description: Check for any instances of R/3 by running r3agent -all. +# The output is scanned for the required info. +# +# The r3agentSYSTEM output is as follows: +# 01 "31G" "QAS" "sapgui sapd370 nr=01 name=QAS" +# +# The r3agentCONFIGURATION output is as follows: +# 01 "DVEBMGS01" "QAS" "sapd370" "" + +function check_for_sap +{ + print "Checking for SAP R/3 instances." + DB_on_this_sys=0 + if (($HTML)) + then + print "" >> /tmp/sysinfo.$$.sap + print "" >> /tmp/sysinfo.$$.sap + print "

" >> /tmp/sysinfo.$$.sap + print "

" >> /tmp/sysinfo.$$.sap + print "TOP

" >> /tmp/sysinfo.$$.sap + print "" >> /tmp/sysinfo.$$.sap + print "SAP R/3 Info

" >> /tmp/sysinfo.$$.sap + print "
"  >> /tmp/sysinfo.$$.sap
+  else
+    print "" >> /tmp/sysinfo.$$.sap
+    print "SAP R/3 Information" >> /tmp/sysinfo.$$.sap
+    print "===================" >> /tmp/sysinfo.$$.sap
+    print "" >> /tmp/sysinfo.$$.sap
+  fi
+  if [ -f ${r3_prog} ]
+  then
+    ${r3_prog} -all
+    num_instances=$( cat ./r3agentSYSTEM | wc -l)
+    if ((num_instances == 0))
+    then
+      print "No R/3 instances found."
+      print "No R/3 instances found." >> /tmp/sysinfo.$$.sap
+      rm ./r3agent[A-Z]* > /dev/null 2>&1
+      break
+    else
+      print "Found ${num_instances} instances."
+      cat ./r3agentCONFIGURATION | 
+      while read inst_id INSTANCE_NAME SAPSYSTEMNAME SAPDBHOST p5 p6 p7
+      do
+        INSTANCE_NAME=$(echo ${INSTANCE_NAME} | sed -e 's/\"*//g')
+        SID=$(echo ${SAPSYSTEMNAME} | sed -e 's/\"*//g')
+        SAPDBHOST=$(echo ${SAPDBHOST} | sed -e 's/\"*//g')
+        SAP_ADM="`echo $SID | $TRUL`adm"
+        startsap=$(su - ${SAP_ADM}  -c  "alias startsap" 2>&1 | tail -1)
+        sapstart=$(ps -ef | grep sapstart | grep -v grep | awk '{print $9}')
+        #PROFILE_DIR=$(grep "^PROFILE_DIR" ${startsap} | awk -F\" '{print $2}')
+        PROFILE_DIR=/usr/sap/${SID}/SYS/profile
+        EXE_DIR=/usr/sap/${SID}/SYS/exe/run
+        startprofile=$(grep "^START_PROFILE" ${startsap} |awk -F\" '{print $2}')
+        #SAPDBHOST=$(awk -F= '/SAPDBHOST/ {print $2}' $PROFILE_DIR/DEFAULT.PFL)
+        ${EXE_DIR}/disp+work -V > /tmp/sysinfo.$$.dispwork 2>&1
+
+        Debug "Saving dispwork -V output file"
+        cp /tmp/sysinfo.$$.dispwork ${r3_dir}/dispwork > /dev/null 2>&1
+
+        r3kernel=$(grep "kernel release" /tmp/sysinfo.$$.dispwork | \
+                    awk '{print $3}')
+        r3patch=$(grep "patch level" /tmp/sysinfo.$$.dispwork | \
+                    head -1 | awk '{print $3}')
+        r3db=$(grep "database kernel" /tmp/sysinfo.$$.dispwork | \
+                    awk '{print $4}')
+        r3dbver=$(grep "database kernel" /tmp/sysinfo.$$.dispwork | \
+                    awk '{print $5}')
+        if [[ -f ${PROFILE_DIR}/${startprofile} ]]
+        then
+          Debug "Saving profile ${PROFILE_DIR}/${startprofile}"
+          cp ${PROFILE_DIR}/${startprofile} ${r3_dir} > /dev/null 2>&1
+        fi
+        if [[ $SAPDBHOST = $sysname ]]
+        then
+          DB_on_this_sys=1
+          startdb=${EXE_DIR}/startsb
+          ORASID="ora`echo $SID | $TRUL`"
+          ORACLE_HOME=/oracle/${SID}
+          db_profile=${ORACLE_HOME}/dbs/init${SID}.ora
+          INITORA=$ORACLE_HOME/dbs/init${SID}.ora
+          if [[ -f ${db_profile} ]]
+          then
+            Debug "Saving profile ${db_profile}"
+            cp ${db_profile} ${r3_dir} > /dev/null 2>&1
+          fi
+        fi
+        print "  Instance ID     = $inst_id"         >> /tmp/sysinfo.$$.sap
+        print "  Instance Name   = ${INSTANCE_NAME}" >> /tmp/sysinfo.$$.sap
+        print "  SAP System Name = ${SID}"           >> /tmp/sysinfo.$$.sap
+        print "  R/3 Kernel Vers = ${r3kernel}"      >> /tmp/sysinfo.$$.sap
+        print "  R/3 Patch Level = ${r3patch}"       >> /tmp/sysinfo.$$.sap
+        print "  R/3 DB Vendor   = ${r3db}"          >> /tmp/sysinfo.$$.sap
+        print "  R/3 DB Version  = ${r3dbver}"       >> /tmp/sysinfo.$$.sap
+        print "  SAP adm user    = ${SAP_ADM}"       >> /tmp/sysinfo.$$.sap
+        print "  startsap        = ${startsap}"      >> /tmp/sysinfo.$$.sap
+        print "  sapstart        = ${sapstart}"      >> /tmp/sysinfo.$$.sap
+        print "  Profile Dir     = ${PROFILE_DIR}"   >> /tmp/sysinfo.$$.sap
+        print "  startprofile    = ${startprofile}"  >> /tmp/sysinfo.$$.sap
+        print "  SAPDBHOST       = ${SAPDBHOST}"     >> /tmp/sysinfo.$$.sap
+        if (($DB_on_this_sys))
+        then
+          print "  DB_on_this_sys  = true"           >> /tmp/sysinfo.$$.sap
+          print "    ORASID        = ${ORASID}"      >> /tmp/sysinfo.$$.sap
+          print "    ORACLE_HOME   = ${ORACLE_HOME}" >> /tmp/sysinfo.$$.sap
+          print "    INITORA       = ${INITORA}"     >> /tmp/sysinfo.$$.sap
+        else
+          print "  DB_on_this_sys  = false"          >> /tmp/sysinfo.$$.sap
+        fi
+        print "" >> /tmp/sysinfo.$$.sap
+      done
+    fi
+  else
+    print "  r3agent not found!"
+    print "  Scanning for dw.sap processes."
+    allinst=$(ps -ef | grep dw.sap | grep -v grep | awk '{print $(NF -1)}' | sort | uniq)
+    if [[ -z $allinst ]]
+    then
+      num_instances=0
+    else
+      num_instances=$(echo $allinst | wc -w)
+    fi
+    if ((num_instances == 0))
+    then
+      print "No R/3 instances found."
+      print "No R/3 instances found." >> /tmp/sysinfo.$$.sap
+      break
+    else
+      print "Found ${num_instances} instances."
+      for inst in ${allinst}
+      do
+        numchar=$(echo $inst | wc -c)
+        (( numchar = numchar - 1 ))
+        (( numchar1 = numchar - 1 ))
+        SID=$(echo $inst | cut -b7-9)
+        inst_id=$(echo $inst | cut -b${numchar1}-${numchar})
+        INSTANCE_NAME=$(echo $inst | cut -b11-${numchar})
+        SAP_ADM="`echo $SID | $TRUL`adm"
+        PROFILE_DIR=/usr/sap/${SID}/SYS/profile
+        startsap=$(su - ${SAP_ADM}  -c  "alias startsap" 2>&1 | tail -1)
+        #sapstart=$(ps -ef | grep sapstart | grep $SID |grep -v grep | awk '{print $9}')
+        sapstart=$(ls -L ${PROFILE_DIR}/START_${INSTANCE_NAME})
+        #echo $sapstart
+        startprofile=$(grep "^START_PROFILE" ${sapstart} |awk -F\" '{print $2}')
+        EXE_DIR=/usr/sap/${SID}/SYS/exe/run
+        SAPDBHOST=$(awk -F= '/SAPDBHOST/ {print $2}' $PROFILE_DIR/DEFAULT.PFL)
+        SAPDBHOST=$(echo $SAPDBHOST | sed -e 's/\ *//g')
+        if [[ $SAPDBHOST = $sysname ]]
+        then
+          DB_on_this_sys=1
+          startdb=${EXE_DIR}/startsb
+          ORASID="ora`echo $SID | $TRUL`"
+          ORACLE_HOME=/oracle/${SID}
+          INITORA=$ORACLE_HOME/dbs/init${SID}.ora
+          if [[ -f ${db_profile} ]]
+          then
+            Debug "Saving profile ${db_profile}"
+            cp ${db_profile} ${r3_dir} > /dev/null 2>&1
+          fi
+        fi
+        ${EXE_DIR}/disp+work -V > /tmp/sysinfo.$$.dispwork 2>&1
+
+        Debug "Saving dispwork -V output file"
+        cp /tmp/sysinfo.$$.dispwork ${r3_dir}/dispwork > /dev/null 2>&1
+
+        r3kernel=$(grep "kernel release" /tmp/sysinfo.$$.dispwork | \
+                    awk '{print $3}')
+        r3patch=$(grep "patch level" /tmp/sysinfo.$$.dispwork | \
+                    head -1 | awk '{print $3}')
+        r3db=$(grep "database kernel" /tmp/sysinfo.$$.dispwork | \
+                    awk '{print $4}')
+        r3dbver=$(grep "database kernel" /tmp/sysinfo.$$.dispwork | \
+                    awk '{print $5}')
+
+        print "  Instance ID     = $inst_id"         >> /tmp/sysinfo.$$.sap
+        print "  Instance Name   = ${INSTANCE_NAME}" >> /tmp/sysinfo.$$.sap
+        print "  SAP System Name = ${SID}" >> /tmp/sysinfo.$$.sap
+        print "  R/3 Kernel Vers = ${r3kernel}"      >> /tmp/sysinfo.$$.sap
+        print "  R/3 Patch Level = ${r3patch}"       >> /tmp/sysinfo.$$.sap
+        print "  R/3 DB Vendor   = ${r3db}"          >> /tmp/sysinfo.$$.sap
+        print "  R/3 DB Version  = ${r3dbver}"       >> /tmp/sysinfo.$$.sap
+        print "  SAP adm user    = ${SAP_ADM}"       >> /tmp/sysinfo.$$.sap
+        print "  startsap        = ${startsap}"      >> /tmp/sysinfo.$$.sap
+        print "  sapstart        = ${sapstart}"      >> /tmp/sysinfo.$$.sap
+        print "  Profile Dir     = ${PROFILE_DIR}"   >> /tmp/sysinfo.$$.sap
+        print "  startprofile    = ${startprofile}"  >> /tmp/sysinfo.$$.sap
+        print "  SAPDBHOST       = ${SAPDBHOST}"     >> /tmp/sysinfo.$$.sap
+        if (($DB_on_this_sys))
+        then
+          print "  DB_on_this_sys  = true" >> /tmp/sysinfo.$$.sap
+          print "    ORASID        = ${ORASID}" >> /tmp/sysinfo.$$.sap
+          print "    ORACLE_HOME   = ${ORACLE_HOME}" >> /tmp/sysinfo.$$.sap
+          print "    INITORA       = ${INITORA}" >> /tmp/sysinfo.$$.sap
+        else
+          print "  DB_on_this_sys  = false" >> /tmp/sysinfo.$$.sap
+        fi
+        print "" >> /tmp/sysinfo.$$.sap
+      done
+    fi
+  fi
+
+}
+
+###############################################################################
+# Function:    chk_ignite()
+# Description: Check if IgniteUX is installed. If so, check for the last
+#              completed make_recovery. Check if make_recovery -C has been
+#              run. If so, run check_recovery and display results.
+#
+# Arguments:   none
+# Returns:     none
+
+function chk_ignite
+{
+  Debug "  Starting chk_ignite"
+  if [[ ${osmajor} <  10 ]]
+  then
+    print "IgniteUX is not supported on less that HP-UX 10.x"
+    Debug "  Unsupported HP-UX version $osmajor"
+    break
+  fi
+  print "Checking for IgniteUX."
+  if [ -d /opt/ignite ]
+  then
+    if [ -f /opt/ignite/Version ]
+    then
+      ignite_ver=$(cat /opt/ignite/Version)
+      print "IgniteUX is installed. Version = $ignite_ver" >> \
+                   /tmp/sysinfo.$$.ignite
+    else
+      print "IgniteUX is installed. Version = N/A." \
+                >> /tmp/sysinfo.$$.ignite
+    fi
+    if [ -f /var/opt/ignite/logs/makerec.log1 ]
+    then
+      last_make_recovery=$(grep Completed /var/opt/ignite/logs/makerec.log1)
+      if [ -z $last_make_recovery ]
+      then
+        print "WARNING (${sysname}): IGNITE make_recovery not run." >> \
+                 /tmp/sysinfo.$$.errwarn
+      else
+        print "Most recent make_recovery  = ${last_make_recovery}" >> \
+                 /tmp/sysinfo.$$.ignite
+      fi
+    else
+      print "WARNING (${sysname}): IGNITE make_recovery not run or logfile deleted." \
+              >> /tmp/sysinfo.$$.errwarn
+    fi
+    if [ -f /var/opt/ignite/recovery/makerec.last ]
+    then
+      check_recovery #> /dev/null 2>&1
+    else
+      print "WARNING (${sysname}): IGNITE check_recovery log not found"  \
+              >> /tmp/sysinfo.$$.errwarn
+    fi
+  else
+    print "WARNING (${sysname}): IgniteUX is NOT installed." \
+            >> /tmp/sysinfo.$$.errwarn
+  fi
+  f_display_file /tmp/sysinfo.$$.ignite
+}
+###############################################################################
+# Function:    chk_diags()
+# Description: Check the system's diagnostic software configuration.  Output
+#              the predictive setting if its installed. This is important info.
+#
+# Arguments:   none
+# Returns:     none
+
+chk_diags()
+{
+  Debug "  Starting Chk_Diags. OSMAJOR = $osmajor"
+
+  if (($HTML))
+  then
+    print "
" >> /tmp/sysinfo.$$.diagnostics.info + print "" >> \ + /tmp/sysinfo.$$.diagnostics.info + print "

" >> \ + /tmp/sysinfo.$$.diagnostics.info + print "

TOP

" >> \ + /tmp/sysinfo.$$.diagnostics.info + print "" >> \ + /tmp/sysinfo.$$.diagnostics.info + print "System Diagnostics Settings

" >> \ + /tmp/sysinfo.$$.diagnostics.info + print "
"   >> \
+               /tmp/sysinfo.$$.diagnostics.info
+  else
+    print "SYSTEM DIAGNOSTICS SETTINGS" >> \
+               /tmp/sysinfo.$$.diagnostics.info
+    print "==========================" >> \
+               /tmp/sysinfo.$$.diagnostics.info
+  fi
+
+  print -n "checking system diagnostics "
+
+  print "" >> /tmp/sysinfo.$$.diagnostics.info
+  case $osmajor in
+    10 | 11 )
+#
+# Look for the OnlineDiag product file. If it exists, then the product is
+# installed on the system. You can use the product's INDEX file to get the
+# product version (this can be important info).
+#
+      print ".\c"
+      prodfile=$(ls -d /var/adm/sw/products/OnlineDiag* 2>&1)
+      Debug "Found OnlineDiag dir - ${prodfile}"
+      if [ -d "$prodfile" ]
+      then
+        print -n "Online Diagnostics are Installed: " \
+                  >> /tmp/sysinfo.$$.diagnostics.info
+        print "Version: $(grep ^revision $prodfile/pfiles/INDEX \
+            | awk '{print $NF}')\t\c" >> /tmp/sysinfo.$$.diagnostics.info
+        print "Status: \c" >> /tmp/sysinfo.$$.diagnostics.info
+#
+# Now use the PS command to see if diagnostics are running.
+#
+       ps -ef | grep -q diagmon
+       if [ $? -eq 0 ]
+       then
+         print "Running" >> /tmp/sysinfo.$$.diagnostics.info
+       else
+         print "Down" >> /tmp/sysinfo.$$.diagnostics.info
+       fi
+       print "" >> /tmp/sysinfo.$$.diagnostics.info
+#
+# If the diagnostics software is properly installed then there should be
+# programs in the LIF area named ODE, MAPPER, and MAPFILE. If these values
+# are not present, then diags will not run from console mode.
+#
+# By checking all boot disks (obtained from lvlnboot -v), we also check
+# any mirrored volumes. This is good because mirrored root volumes are
+# often missed.
+#
+       typeset -i liferrors
+       bootdsks=$(lvlnboot -v 2>/dev/null | grep "Boot Disk" | awk '{print $1}')
+
+       print "Checking LIF Information (ODE, MAPPER and, MAPFILE)" >> \
+              /tmp/sysinfo.$$.diagnostics.info
+       print ".\c"
+#
+# Now loop for each boot disk found by lvlnboot -v. This will check mirrors
+# as well as primary disks. All boot disks should have diags installed.
+#
+       for bdisk in $bootdsks
+       do
+         liferrors=0
+         lifls -l $bdisk >> /tmp/sysinfo.$$.diagnostics.lifinfo
+         for lfdiag in ODE MAPPER MAPFILE
+         do
+           grep -q $lfdiag /tmp/sysinfo.$$.diagnostics.lifinfo
+           if [ $? -ne 0 ]
+           then
+             print "WARNING (${sysname}): Missing $lfdiag on ${bdisk}." \
+                  >> /tmp/sysinfo.$$.errwarn
+             liferrors=liferrors+1
+           fi
+         done
+#
+# If the liferrors variable is greater than zero then this disk was missing at
+# least one of the required LIF files. If its equal to zero, then this disk
+# has all the required info.
+#
+         if [ $liferrors -eq 0 ]
+         then
+           print "$bdisk\tOK" >> /tmp/sysinfo.$$.diagnostics.info
+         fi
+        done
+      else
+      print "WARNING (${sysname}): Online Diagnostics are NOT Installed." >> \
+            /tmp/sysinfo.$$.errwarn
+      print "Online Diagnostics are NOT Installed." >> \
+            /tmp/sysinfo.$$.diagnostics.info
+      fi
+#
+# Now check the system product database to see if Predictive is installed. 
+# If it is, then output its version info, and check to see if its running 
+# (again using the ps command).
+#
+      print ".\c"
+      print " " >> /tmp/sysinfo.$$.diagnostics.info
+      prodfile=$(ls -d /var/adm/sw/products/Predictive* 2>&1)
+      Debug "Found Predictive dir - ${prodfile}"
+      if [ -d "$prodfile" ]
+      then
+        print -n "Predictive is Installed: Version:" \
+             >> /tmp/sysinfo.$$.diagnostics.info
+        print " $(grep ^revision $prodfile/pfiles/INDEX | awk \
+            '{print $NF}')\t\c" >> /tmp/sysinfo.$$.diagnostics.info
+        print "Status: \c" >> /tmp/sysinfo.$$.diagnostics.info
+        ps -ef | grep -q psmond
+        if [ $? -eq 0 ]
+        then
+          print "Running" >> /tmp/sysinfo.$$.diagnostics.info
+        else
+          print "Down" >> /tmp/sysinfo.$$.diagnostics.info
+        fi
+#
+# Predictive is installed. Run PSCONFIG to output the configuration info and
+# the recent log history. This is good documentation of the system, and shows
+# whether the system has been reporting problems to the response center.
+#
+        print ".\c"
+        print "" >> /tmp/sysinfo.$$.diagnostics.info
+        print "Predictive Configuration Information" >> \
+                     /tmp/sysinfo.$$.diagnostics.info 
+        if [ -x /opt/pred/bin/PSCONFIG ]
+        then
+          print ".\c"
+          /opt/pred/bin/PSCONFIG print configuration >> \
+                     /tmp/sysinfo.$$.diagnostics.info 2>/dev/null
+          print ".\c"
+          /opt/pred/bin/PSCONFIG print action >> \
+                     /tmp/sysinfo.$$.diagnostics.info 2>/dev/null
+        else
+          print "ARNING (${sysname}): PSCONFIG is not executable" >> \
+                        /tmp/sysinfo.$$.errwarn
+        fi
+      else
+        print "Predictive is NOT Installed." >> \
+                    /tmp/sysinfo.$$.diagnostics.info
+        print "WARNING (${sysname}): Predictive is NOT Installed." >> \
+                    /tmp/sysinfo.$$.errwarn
+      fi
+    ;;
+#
+# I did not implement any code which would work with V9.x or earlier. At this
+# point, most customers should be at V10.x by now. V9.x is off support life.
+#
+    8 | 9 )
+      print "This module was created for V10.x and higher. Sorry."
+    ;;
+  esac
+
+  print ""
+  print "" >> /tmp/sysinfo.$$.diagnostics.info
+
+  f_display_file /tmp/sysinfo.$$.diagnostics.info
+
+  return $return_code
+}
+
+###############################################################################
+# Function:    chk_sysaccess()
+# Description: Routine used to check some of the security characteristics of
+#              the system. This includes checking the permissions of the
+#              /etc/passwd, and /etc/group files as well as checking for
+#              .rhosts and other files.
+# Arguments:   none
+# Returns:     none
+
+chk_sysaccess() {
+  Debug "  Starting Chk_SysAccess. OSMAJOR = $osmajor"
+
+  if (($HTML))
+  then
+    print "
" >> /tmp/sysinfo.$$.sysaccess.info + print "" >> /tmp/sysinfo.$$.sysaccess.info + print "

" >> /tmp/sysinfo.$$.sysaccess.info + print "

" >> /tmp/sysinfo.$$.sysaccess.info + print "TOP

" >> /tmp/sysinfo.$$.sysaccess.info + print "" >> /tmp/sysinfo.$$.sysaccess.info + print "System Security Audit

" >> /tmp/sysinfo.$$.sysaccess.info + print "
" >> /tmp/sysinfo.$$.sysaccess.info
+  else
+    print "SYSTEM SECURITY AUDIT" >> /tmp/sysinfo.$$.sysaccess.info
+    print "==========================" >> /tmp/sysinfo.$$.sysaccess.info
+  fi
+
+  last -R > /tmp/sysinfo.$$.last 
+  print -n "checking system security "
+  print "" >> /tmp/sysinfo.$$.sysaccess.info
+
+  print "File Permissions. Files which should be World and Group Read-Only." \
+           >>  /tmp/sysinfo.$$.sysaccess.info
+
+  for lfile in $WORLDREADFILES
+  do
+    Debug "   chk_SysAccess. Checking $lfile."
+    if [[ ! -a "$lfile" ]]
+    then
+      print "ERROR ($sysname): Important file $lfile does not exist." >> \
+              /tmp/sysinfo.$$.errwarn
+      return_code=$ERROR
+    else
+#
+# Get the important file's directory information.
+#
+      llinfo=$(ll -d $lfile)
+#
+# Now check the file's permissions
+#
+      echo "$llinfo" | cut -c5-10 | egrep -q "w"
+      if [ $? -eq 0 ]
+      then
+        print "WARNING ($sysname): Important file $lfile has Group or World WRITE access.  " >> /tmp/sysinfo.$$.errwarn
+        return_code=$WARN
+      fi
+    echo "$llinfo" >> /tmp/sysinfo.$$.sysaccess.info
+    fi
+  done
+#
+# Now check the system for files/directories which should be accessible to
+# root only. This code will flag files which have any access other than the
+# owner.
+#
+  print "" >> /tmp/sysinfo.$$.sysaccess.info
+  print "File Permissions. Files which should be Accessible to Root only." >> \
+             /tmp/sysinfo.$$.sysaccess.info
+
+  for lfile in $NOREADFILES
+  do
+    Debug "   chk_SysAccess. Checking $lfile."
+    if [[ ! -a "$lfile" ]]
+    then
+      print "WARNING ($sysname): Important file $lfile does not exist." >> \
+               /tmp/sysinfo.$$.errwarn
+      return_code=$WARN
+    else
+#
+# Get the Important file's directory information.
+#
+      llinfo=$(ll -d $lfile)
+#
+# Now check the file's permissions
+#
+      echo "$llinfo" | cut -c5-10 | egrep -q "r|w|x"
+      if [ $? -eq 0 ]
+      then
+        print "WARNING ($sysname): Important file $lfile has Group or World access." \
+                >> /tmp/sysinfo.$$.errwarn
+        return_code=$WARN
+      fi
+      echo "$llinfo" >> /tmp/sysinfo.$$.sysaccess.info
+    fi
+  done
+
+  print "" >> /tmp/sysinfo.$$.sysaccess.info
+  print "Check for Important Audit Files:" >> /tmp/sysinfo.$$.sysaccess.info
+#
+# Now check for the wtmp and btmp files. These are important to login history.
+# make sure they exist, if not, then give a warning. then check their security.
+# The wtmp file tracks login history. The btmp file tracks invalid logons.
+#
+# Also check the sulog file. This file tracks the history of SU'ing to root.
+#
+  case $osmajor in
+    8 | 9 )
+      WTMP="/etc/wtmp"
+      BTMP="/etc/btmp"
+      SULOG="/usr/adm/sulog"
+    ;;
+
+    * )
+      WTMP="/var/adm/wtmp"
+      BTMP="/var/adm/btmp"
+      SULOG="/var/adm/sulog"
+    ;;
+  esac
+#
+# Check the wtmp file
+#
+  if [[ ! -a "$WTMP" ]]
+  then
+    print "WARNING ($sysname): Audit File $WTMP does not exist." >> \
+                /tmp/sysinfo.$$.errwarn
+    print "If you create $WTMP you can track logon history." >> \
+                /tmp/sysinfo.$$.errwarn
+    print " " >> /tmp/sysinfo.$$.errwarn
+  else
+    llinfo=$(ll -d $WTMP)
+
+    echo "$llinfo" | cut -c8-10 | egrep -q "w"
+    if [ $? -eq 0 ]
+    then
+      print -n "WARNING ($sysname): Audit File $WTMP has Group or " >> \
+                  /tmp/sysinfo.$$.errwarn
+      print "World WRITE access." >> /tmp/sysinfo.$$.errwarn
+      print "   $WTMP should have at most World read access." >> \
+                  /tmp/sysinfo.$$.errwarn
+      print " " >> /tmp/sysinfo.$$.errwarn
+    fi
+    echo "$llinfo" >> /tmp/sysinfo.$$.sysaccess.info
+  fi
+#
+# Now check the btmp file.
+#
+  if [[ ! -a "$BTMP" ]]
+  then
+    print "WARNING ($sysname): Audit File $BTMP does not exist." >> \
+               /tmp/sysinfo.$$.errwarn
+    print "If you create $BTMP you can track invalid logon history." >> \
+               /tmp/sysinfo.$$.sysaccess.warn
+    print " " >> /tmp/sysinfo.$$.sysaccess.warn
+  else
+    llinfo=$(ll -d $BTMP)
+
+    echo "$llinfo" | cut -c5-10 | egrep -q "w"
+    if [ $? -eq 0 ]
+    then
+      print -n "WARNING ($sysname): Audit File $BTMP has Group or ">> \
+                  /tmp/sysinfo.$$.errwarn
+      print "World WRITE access." >> /tmp/sysinfo.$$.errwarn
+      print "$BTMP should have at most Group or World read access." >> \
+                  /tmp/sysinfo.$$.errwarn
+      print " " >> /tmp/sysinfo.$$.errwarn
+    fi
+
+    echo "$llinfo" >> /tmp/sysinfo.$$.sysaccess.info
+  fi
+#
+# Check the sulog file now.
+#
+  if [[ ! -a "$SULOG" ]]
+  then
+    print "WARNING ($sysname): Audit File $SULOG does not exist." >> \
+        /tmp/sysinfo.$$.errwarn
+    print "If you create $SULOG you can track su logon history." >> \
+         /tmp/sysinfo.$$.sysaccess.warn
+    print " " >> /tmp/sysinfo.$$.sysaccess.warn
+  else
+    llinfo=$(ll -d $SULOG)
+
+    echo "$llinfo" | cut -c5-10 | egrep -q "w"
+    if [ $? -eq 0 ]
+    then
+      print -n "WARNING ($sysname): Audit File $SULOG has Group or " >> \
+                   /tmp/sysinfo.$$.errwarn
+      print "World WRITE access." >> /tmp/sysinfo.$$.sysaccess.warn
+      print "$SULOG should have at most Group or World read access." >> \
+                  /tmp/sysinfo.$$.sysaccess.warn
+      print " " >> /tmp/sysinfo.$$.sysaccess.warn
+    fi
+
+    echo "$llinfo" >> /tmp/sysinfo.$$.sysaccess.info
+    print " " >> /tmp/sysinfo.$$.sysaccess.info
+    print "Five Most Recent SUlog entries:" >> /tmp/sysinfo.$$.sysaccess.info
+    tail -5 $SULOG >> /tmp/sysinfo.$$.sysaccess.info
+  fi
+#
+# Check the validity of the /etc/passwd and /etc/group files by running
+# the pwck and grpck commands. This will help identify illegal password and
+# group entries. Also, check the /etc/passwd file for entries missing
+# passwords.
+#
+  if [[ -x "/etc/pwck" ]]
+  then
+    print " " >> /tmp/sysinfo.$$.sysaccess.info
+    print "Running Passwd Check Script:" >> /tmp/sysinfo.$$.sysaccess.info
+    /etc/pwck >> /tmp/sysinfo.$$.sysaccess.info 2>&1
+  fi
+#
+# Check the password file for entries w/o a password.
+#
+  awk -F: -v sysname=$sysname ' $2 == "" { printf("\nERROR. (%s).  NULL Password found in /etc/passwd\nPassword Record = %s\n\n",sysname,$0)}' /etc/passwd >> \
+             /tmp/sysinfo.$$.errwarn 2>&1
+
+  if [[ -x "/etc/grpck" ]]
+  then
+    print " " >> /tmp/sysinfo.$$.sysaccess.info
+    print "Running Group Check Script:" >> /tmp/sysinfo.$$.sysaccess.info
+    /etc/grpck >> /tmp/sysinfo.$$.sysaccess.info 2>&1
+
+  fi
+#
+# Now check for an .rhost file definition for root.
+# If this file exists, then print a message since this is a potential security
+# issue.
+#
+  if [[ -a "/.rhosts" ]]
+  then
+    print "WARNING ($sysname): Root has a /.rhosts file." >> \
+               /tmp/sysinfo.$$.errwarn
+    print "This file allows users from other systems to logon as root w/o a password." >> /tmp/sysinfo.$$.errwarn
+    print " " >> /tmp/sysinfo.$$.errwarn
+
+    llinfo=$(ll -d /.rhosts)
+
+    echo "$llinfo" | cut -c5-10 | egrep -q "r|w|x"
+    if [ $? -eq 0 ]
+    then
+      print "WARNING ($sysname): The /.rhosts file has Group or World access." \
+             >> /tmp/sysinfo.$$.errwarn
+      print "If this existence if this file is required then ONLY root should have access." >> /tmp/sysinfo.$$.errwarn
+      print " " >> /tmp/sysinfo.$$.errwarn
+    fi
+
+    echo "$llinfo" >> /tmp/sysinfo.$$.sysaccess.warn
+    print "" >> /tmp/sysinfo.$$.sysaccess.warn
+    print "Contents of /.rhosts file:" >> /tmp/sysinfo.$$.sysaccess.warn
+    cat /.rhosts >> /tmp/sysinfo.$$.sysaccess.warn
+  fi
+#
+# Now check the system for modem definitions. This first pass will not include
+# uucp items. I'm checking for ttyd* files in the /dev directory, then I'll
+# campare any matches to the /etc/inittab file. This will allow me to
+# look for getty processes.
+#
+  print " " >> /tmp/sysinfo.$$.sysaccess.info
+  print "Dialin/Modem Definitions:" >> /tmp/sysinfo.$$.sysaccess.info
+  print " " >> /tmp/sysinfo.$$.sysaccess.info
+
+  MLIST=$(ls /dev/ttyd*  2>&1)
+
+  Debug "  chk_SysAccess. MLIST = $MLIST"
+  for modtty in $MLIST
+  do
+    modttyb=$(basename $modtty)
+    Debug "  chk_SysAccess. Checking = $modtty"
+#
+# Check the /etc/inittab file for the terminal entry. Exclude lines which
+# begin with a '#'. Include lines which contain 'getty'
+#
+    gettyinfo=$( grep -v '^#' /etc/inittab | grep 'getty' | grep -i $modttyb )
+    gettystate=$( echo $gettyinfo | awk -F: '{print $3}' )
+
+    Debug "  chk_SysAccess. Getty Info/State = $gettyinfo $gettystate"
+    case $gettystate in
+      respawn )
+        Debug " chkSysAccess. Respawn in getty"
+        ps -ef | grep getty | grep -q $modttyb
+        if [ $? -eq 0 ]
+        then
+          Debug "Found Getty for $modttyb"
+          print "Modem: $modttyb (waiting for a connection)" >> \
+             /tmp/sysinfo.$$.sysaccess.info
+        else
+          Debug "No Getty Found for $modttyb"
+          if ps -t $modttyb | tail +2 | wc -l
+          then
+            Debug "No Processes Found for $modttyb"
+            print "Modem: $modttyb (getty missing?)" >> \
+                    /tmp/sysinfo.$$.sysaccess.info
+            print "WARNING ($sysname): $modttyb does not have a getty or a login process" >> /tmp/sysinfo.$$.errwarn
+          else
+            Debug "Modem in Use"
+            print "Modem: $modttyb (currently in use)" >> \
+                     /tmp/sysinfo.$$.sysaccess.info
+          fi
+        fi
+
+        echo $gettyinfo | awk -F: '{printf("Inittab Info: %s\n",$4)}' >> \
+              /tmp/sysinfo.$$.sysaccess.info
+        print " " >> /tmp/sysinfo.$$.sysaccess.info
+        print "Five most recent Logons for $modttyb" >> \
+                 /tmp/sysinfo.$$.sysaccess.info
+        #last -R > /tmp/sysinfo.$$.last
+        grep -i $modttyb /tmp/sysinfo.$$.last | tail -5 >> /tmp/sysinfo.$$.sysaccess.info
+        print " " >> /tmp/sysinfo.$$.sysaccess.info
+      ;;
+
+      off )
+        print "Modem: $modttyb (disabled in /etc/inittab)" >> \
+                    /tmp/sysinfo.$$.sysaccess.info
+      ;;
+
+      * )
+      ;;
+    esac
+  done
+  print " "
+
+  print " " >> /tmp/sysinfo.$$.sysaccess.info
+
+  f_display_file /tmp/sysinfo.$$.sysaccess.info
+  f_display_file /tmp/sysinfo.$$.sysaccess.warn
+
+  return $return_code
+}
+
+
+###############################################################################
+# Function:    chk_logfiles()
+# Description: Routine used to check the system logfiles. We want to make sure
+#              that the logfiles exist, and are not greater than 2 Mbytes.
+# Arguments:   none
+# Returns:     none
+
+chk_logfiles() {
+  Debug "  Starting Chk_Logfiles. OSMAJOR = $osmajor"
+
+  case $osmajor in
+    8 | 9 )
+      FLIST="$LOGFILELISTV9"
+    ;;
+
+    * )
+      FLIST="$LOGFILELISTV10"
+    ;;
+  esac
+
+  Debug " chk_logfiles. FLIST=$FLIST"
+  if (($HTML))
+  then
+    print "
" >> /tmp/sysinfo.$$.logfile.info + print "" >> /tmp/sysinfo.$$.logfile.info + print "

" >> /tmp/sysinfo.$$.logfile.info + print "

" >> /tmp/sysinfo.$$.logfile.info + print "TOP

" >> /tmp/sysinfo.$$.logfile.info + print "" >> /tmp/sysinfo.$$.logfile.info + print "System Logfile Information

" >> /tmp/sysinfo.$$.logfile.info + print "
"  >> /tmp/sysinfo.$$.logfile.info
+  else
+    print "SYSTEM LOGFILE INFORMATION" >> /tmp/sysinfo.$$.logfile.info
+    print "==========================" >> /tmp/sysinfo.$$.logfile.info
+  fi
+  print -n "checking system logfiles"
+  print "" >> /tmp/sysinfo.$$.logfile.info
+  print "Checking System Logfiles. Validating file sizes and Permissions." >> \
+              /tmp/sysinfo.$$.logfile.info
+
+  for lfile in $FLIST
+  do
+    Debug "   chk_logfiles. Checking $lfile."
+    if [[ ! -a "$lfile" ]]
+    then
+      print "ERROR ($sysname): Logfile $lfile does not exist." >> \
+         /tmp/sysinfo.$$.errwarn
+      return_code=$ERROR
+    else
+#
+# Get the logfile's directory information. Then check its size.
+#
+      llinfo=$(ll -d $lfile)
+
+      psize=$( echo $llinfo | awk '{print $5}' )
+      if (( $psize >= 2097152 ))
+      then
+        print "WARNING ($sysname): Logfile $lfile is greater than 2 Mbytes." \
+          >> /tmp/sysinfo.$$.errwarn
+        return_code=$WARN
+      fi
+#
+# Now check the file's permissions
+#
+      echo "$llinfo" | cut -c5-10 | egrep -q "w"
+      if [ $? -eq 0 ]
+      then
+        print "WARNING ($sysname): Logfile $lfile has Group or World WRITE access.  " >> /tmp/sysinfo.$$.errwarn
+        return_code=$WARN
+      fi
+      echo "$llinfo" >> /tmp/sysinfo.$$.logfile.info
+    fi
+  done
+
+  print " " >> /tmp/sysinfo.$$.logfile.info
+#
+# Now check the system's crash directory for crash files. If these files exist
+# there could be space problems later.
+#
+  case $osmajor in
+    10 | 11 )
+      if [[ -a "/etc/rc.config.d/savecrash" ]]
+      then
+        . /etc/rc.config.d/savecrash
+        if [ ! -z "$CRASHDIR" ]
+        then
+          LOGCRASH=$CRASHDIR
+        else
+          LOGCRASH="/var/adm/crash"
+        fi
+      else
+        LOGCRASH="/var/adm/crash"
+      fi
+
+      CRASHFILES=$(ll $LOGCRASH | tail +2)
+      if [ -z "$CRASHFILES" ]
+      then
+        print "There are NO Crash Dumps in $LOGCRASH." >> \
+                   /tmp/sysinfo.$$.logfile.info
+      else
+        print " " >> /tmp/sysinfo.$$.logfile.warn
+        print "WARNING ($sysname): There are files in the Crash Dump directory $LOGCRASH." >> /tmp/sysinfo.$$.errwarn
+        echo "$CRASHFILES" >> /tmp/sysinfo.$$.errwarn
+      fi
+    ;;
+
+  * )
+    ;;
+  esac
+#
+# Now output the contents of the DMESG command. This can provide useful
+# info regarding recent events on the system.
+#
+  print " " >> /tmp/sysinfo.$$.logfile.info
+  print "Ouput of System DMESG Info:" >> /tmp/sysinfo.$$.logfile.info
+  print "___________________________" >> /tmp/sysinfo.$$.logfile.info
+
+  dmesg >> /tmp/sysinfo.$$.logfile.info 2>&1
+
+  print " " >> /tmp/sysinfo.$$.logfile.info
+  print "End of DMESG Output" >> /tmp/sysinfo.$$.logfile.info
+  print "___________________________" >> /tmp/sysinfo.$$.logfile.info
+  print " " >> /tmp/sysinfo.$$.logfile.info
+#
+# Now check the Sendmail Queue for a large number of entries, or entries
+# which are older than two days. Finding entries in the queue could represent
+# a problem.
+#
+  print " " >> /tmp/sysinfo.$$.logfile.info
+  print "Checking Sendmail Queue: \c" >> /tmp/sysinfo.$$.logfile.info
+#
+# Before changing to the Sendmail Queue directory, save our current directory
+# value so we can change back to it when we're finished.
+#
+  OLDPWD=$(pwd)
+  case $osmajor in
+    8 | 9 )
+      mailqdir=/usr/spool/mqueue
+      mailpodir=/usr/mail
+    ;;
+
+    * )
+      mailqdir=/var/spool/mqueue
+      mailpodir=/var/mail
+    ;;
+  esac
+#
+# Go to the Sendmail Queue directory and scan it for entries. Iff some are
+# found then check to see if any are older than 2 days.
+#
+  cd $mailqdir
+  llinfo=$(ll 2>&1 | tail +2)
+
+  if [[ -z "$llinfo" ]]
+  then
+    print "Sendmail Queue is Empty." >> /tmp/sysinfo.$$.logfile.info
+  else
+    print "Sendmail Queue has $(ls | wc -w | awk '{print $1}') entries." >> \
+            /tmp/sysinfo.$$.logfile.info
+    ll 2>&1 | tail +2 >> /tmp/sysinfo.$$.logfile.info
+#
+# Use the find command to list entries older than 2 days
+#
+    findinfo=$(find . -mtime +2 -exec ll -d {} \;)
+    if [[ ! -z "$findinfo" ]]
+    then
+      print " " >> /tmp/sysinfo.$$.logfile.warn
+      print "Sendmail Queue has Entries Older than 2 days." >> \
+                /tmp/sysinfo.$$.logfile.warn
+      find . -mtime +2 -exec ll -d {} \; >> /tmp/sysinfo.$$.logfile.warn
+      print " " >> /tmp/sysinfo.$$.logfile.warn
+    fi
+  fi
+#
+# Now change to the Sendmail Post Office directory. This is the directory
+# where mail is normally stored on the system. Check for entries which are
+# large and very old.
+#
+  cd $mailpodir
+  print " " >> /tmp/sysinfo.$$.logfile.info
+  print "Checking the Mail Post Office Directory: \c" >> \
+              /tmp/sysinfo.$$.logfile.info
+
+  llinfo=$(ls 2>&1)
+  if [[ -z "$llinfo" ]]
+  then
+    print "No Entries Found." >> /tmp/sysinfo.$$.logfile.info
+  else
+    print "Found $(echo $llinfo | wc -w) Entries." >> \
+               /tmp/sysinfo.$$.logfile.info
+    ll 2>&1 >> /tmp/sysinfo.$$.logfile.info
+#
+# Now that we have found entries in the directory, check to see if they are
+# larger than 2 Mbytes.
+#
+    find . -size +4096 -exec ll -d {} \; > /tmp/sysinfo.$$.logfile.tmp 2>&1
+    if [[ -s "/tmp/sysinfo.$$.logfile.tmp" ]]
+    then
+      print " " >> /tmp/sysinfo.$$.logfile.warn
+      print "WARNING ($sysname): Post Office Directory has Files Larger than 2 Mbytes." >> /tmp/sysinfo.$$.errwarn
+      cat /tmp/sysinfo.$$.logfile.tmp >> /tmp/sysinfo.$$.errwarn
+      print " " >> /tmp/sysinfo.$$.errwarn
+    fi
+#
+# Now check for entries which have not been access in 90 days. These files
+# may be considered obsolete.
+#
+    find . -mtime +90 -exec ll -d {} \; > /tmp/sysinfo.$$.logfile.tmp 2>&1
+    if [[ -s "/tmp/sysinfo.$$.logfile.tmp" ]]
+    then
+      print " " >> /tmp/sysinfo.$$.logfile.warn
+      print "WARNING ($sysname): Post Office Directory has Files Older than 90 days." >> /tmp/sysinfo.$$.errwarn
+      cat /tmp/sysinfo.$$.logfile.tmp >> /tmp/sysinfo.$$.errwarn
+      print " " >> /tmp/sysinfo.$$.errwarn
+    fi
+  fi
+#
+# Change back to our original directory
+#
+  cd $OLDPWD
+
+  print " " >> /tmp/sysinfo.$$.logfile.info
+  print " "
+
+  f_display_file /tmp/sysinfo.$$.logfile.info
+  f_display_file /tmp/sysinfo.$$.logfile.warn
+
+  return $return_code
+}
+
+
+
+################################################################################
+# Function:    LookupName()
+# Description: Lookup the full name for a specified hostname.  Uses nslookup.
+# Arguments:   
+# Output:      fully qualified hostname
+# Returns:     0: successfully determined name
+#              1: unable to find name (using /etc/hosts)
+#              2: unable to find name (using name server)
+
+LookupName() {
+
+#   if [ $# -ne 1 ]
+#   then
+#       Panic "${0}: expect  argument"
+#   fi
+
+    #nslookup "$1" > $_TMP 2>&1
+    # Return 0 if name found, 1 if not found and using /etc/hosts,
+    # 2 if not found and using some sort of name server.
+    #
+    nslookup "$1" 2>&1 | 
+    awk '
+         BEGIN              { ret_val=2 }
+         $0 ~ "/etc/hosts"  { ret_val=1 }
+         $1 == "Name:"      { print $2; ret_val=0 }
+         END                { exit (ret_val) }
+        ' $_TMP
+    return_value=$?
+    case $return_value in
+      0)  return_string="found using nslookup";;
+      1)  return_string="found in /etc/hosts/";;
+      2)  return_string="not found";;
+    esac
+    Debug "  return from LookupName = $return_value ($return_string)"
+    return $return_value
+
+}
+
+################################################################################
+# GetCurrentDNS()
+GetCurrentDNS() {
+
+### Changed 6/16/99 J.Semroc - allows for possibility of multiple nameservers,
+###                            domain search directive, and possibility that
+###                            no domain is specified
+###  07/12/99 - added -s option to cut in case nslookup does not return
+###          fully qualified name.  
+###          added ^ to nameserver to ignore commented entries
+
+    if [ -f $RESOLV_CONF ]
+    then
+      CURRENT_DOMAIN=$(nslookup ${sysname} | grep "^Name: " | \
+            awk -F: '{print $2}'| cut -s -d. -f2- )
+      if [[ -z ${CURRENT_DOMAIN} ]]
+      then
+        Debug "Could not find domain using nslookup - checking resolv.conf"
+# Extract the domain from resolv.conf
+        set -- $(awk '
+          $1 == "domain"     { DOMAIN=$2 }
+          $1 == "nameserver" { ADDRESS=$2 ; exit }
+          END { printf "%s %s\n", DOMAIN, ADDRESS }' $RESOLV_CONF)
+        CURRENT_DOMAIN="$1"
+      else
+        Debug "Found domain using nslookup"
+      fi
+
+### Extract the  address and name of nameserver from resolv.conf
+###     if [ $# -eq 2 ]
+###     then
+###         # See if we can get a name for the nameserver
+###         CURRENT_DOMAIN="$1"
+###         CURRENT_DNS_SERVER_IP="$2"
+###         CURRENT_DNS_SERVER=$(LookupName $CURRENT_DNS_SERVER_IP)
+###         if [ -z "$CURRENT_DNS_SERVER" ]
+###         then
+###             CURRENT_DNS_SERVER=unknown
+###         fi
+###     else
+###         unset CURRENT_DOMAIN CURRENT_DNS_SERVER_IP CURRENT_DNS_SERVER
+###    fi
+
+      CURRENT_DNS_SERVER_IP=$(for server in `grep ^nameserver \
+              ${RESOLV_CONF} | awk '{print $2}'`
+               do
+                  print -n $server"  "
+               done | awk '{ print $1}')
+      CURRENT_DNS_SERVER=$(LookupName `echo ${CURRENT_DNS_SERVER_IP} \
+              | awk '{ print $1}' `)
+    fi
+
+}
+
+#===================================================================
+# weed_targets
+#
+# Scan the output from ioscan and filter out all SCSI "target"
+# lines.  Also filter out entries with a low_level driver of "root"
+#===================================================================
+weed_targets()
+{
+    awk -F: '
+        {
+            class  = $9;
+            low_level_driver = $10
+            if (class != "target" && low_level_driver != "root")
+                print $0
+        }' -
+}
+
+
+#==================================================================
+# This routine invokes ioscan with any passed in arguments and
+# generates the above mentioned colon separated output. Note that
+# all lines begin and end with colons. This makes for easy parsing
+# of the output to match specific fields via grep.
+#===================================================================
+formatter()
+{
+    awk -F: '
+        {
+#=====================================================================
+# Not all of the fields of ioscan output are used by SAM.
+# They are all listed here for documentation purposes.
+#=====================================================================
+#       bus_type               = $1
+        cdio                   = $2
+#       is_block               = $3
+#       is_char                = $4
+#       is_pseduo              = $5
+#       block_major_number     = $6
+#       character_major_number = $7
+#       minor_number           = $8
+        class                  = $9
+#       low_level_driver       = $10
+        full_hw                = $11
+        identify_bytes         = $12
+#       device instance        = $13
+#       module_path            = $14
+        driver                 = $15
+        sw_status              = $16
+#       hardware_type          = $17
+        ident                  = $18
+        instance               = $19
+        num_hw   = split(full_hw, hw_path, ".");
+        address = "";
+        unit    = "";
+#============================================================
+#
+# Identifying bytes for PA Modules contain this information:
+#
+#  Byte
+#  Address   Name            Description
+#  --------- --------------- --------------------------------------
+#  0-1       IODC_HVERSION   Hardware version number
+#  2         IODC_SPA        Soft physical address capability
+#  3         IODC_TYPE       Type of module
+#  4-7       IODC_SVERSION   Software version number
+#  8         IODC_REV        IODC revision
+#  9         IODV_DEP        HVERSION dependent
+#  10-11     RESERVED
+#  12-13     IODC_CHECK      Checksum
+#  14-15     IODC_LENGTH     Length of entry point table
+#
+#============================================================
+
+        split(identify_bytes, id_flds, " ");
+        hversion= (((((id_flds[1]*256)+id_flds[2])*256)+id_flds[3])*256)+id_flds[4];
+        sversion= (((((id_flds[5]*256)+id_flds[6])*256)+id_flds[7])*256)+id_flds[8];
+        if ( id_flds[9] != "" ) {
+                device_id = id_flds[9] " " id_flds[10] " " id_flds[11] " " id_flds[12] " " id_flds[13] " " id_flds[14] " " id_flds[15] " " id_flds[16];
+        }
+        else {
+                device_id = "";
+        }
+        #
+        # Specifically for multi path environments (FiberChannel),
+        # save the driver and last 8 bytes of the identify_bytes
+        # of this device ("ext_bus" class; parent of actual devices)
+        # tacking on to actual devices later.
+        #
+        if ( class == "ext_bus") {
+           if ( ( identify_bytes != "") && (( driver == "fcpmux") || ( driver == "fcpdev"))) {
+                parent_driver = driver;
+                parent_id = device_id;
+           }
+           else {
+                parent_driver=""
+                parent_id=""
+           }
+        }
+
+        #
+        # Set driver to "?" if not known
+        #
+        if ((substr(driver, 1, 1) == "?") || (driver == "pdn0") ||
+            (driver == ""))
+        {
+            #
+            # SPECIAL CASES FOR NON-AUTOCONFIGURABLE DEVICES
+            # Check the hardware identifier field, if it matches
+            # a non-autoconfigurable device and there is no driver
+            # bound to the device, fill in the driver field with the
+            # appropriate driver name flag to true
+            #
+#
+# Change this to use the device description.
+#
+            if (ident == "0x2080") {
+                driver = "pdn0";
+            }
+#
+# Change this to use the device description.
+#
+            else if (ident == "0x2f80") {
+                driver = "pdn0";
+            }
+
+            else if ((substr(driver, 1, 1) == "?") || (driver == "")) {
+                driver = "?";
+            }
+
+            not_configured = 1;
+        } else
+            not_configured = 0;
+
+        if (sw_status != "CLAIMED")
+                not_configured = 1;
+
+        if ((driver == "tape1") || (driver == "tape2")      ||
+            (driver == "lpr0")  || (driver == "lpr1")       ||
+            (driver == "disc3") || (driver == "sflop")      ||
+            (driver == "schgr") || (driver == "autox0")     ||
+            (driver == "cs80")  || (driver == "sdisk")      ||
+            (driver == "stape") || (driver == "disc4")) {
+          address = hw_path[num_hw-1];
+          unit    = hw_path[num_hw];
+        }
+        else if ((driver == "disc1") || (driver == "disc2") ||
+                 (driver == "instr0")|| (driver == "pflop")) {
+          address = hw_path[num_hw];
+        }
+
+
+#       printf(":%s:%s:%s:%s:%s:%s:%d:%s:0x%x;0x%x:%s:%s:%s:%s:\n",
+#           cdio, driver, instance, full_hw, address, unit,
+#           not_configured, sw_status, sversion, hversion, ident,
+#           device_id, parent_driver, parent_id );
+        printf("%22s %12s  %s\n", full_hw, driver, ident );
+    }' -
+} # end of formatter
+
+#===========================================================
+# lvm_scan
+#===========================================================
+function lvm_scan
+{
+  # first get all volume groups, logical volumes & physical volumes
+  vgdisplay -v 2>&1 | \
+  awk '
+    BEGIN             { vgprinted = ""; onpvg = ""; numpvgs = 0
+                      }
+    /^VG Name/        {
+                          vg = substr($3, 6, length($3) - 5);
+                          vgprinted = ""; onpvg = ""; numpvgs = 0;
+                          freepe_count = 0; next;
+                      }
+    /VG Write Access/ {
+                           vgwrite = $4;
+                           next;
+                      }
+    /VG Status/       {
+                         vgstatus = "";
+                         for(i=3;i<=NF;i++)
+                            vgstatus = vgstatus $i;
+                         vgstatus = vgstatus "," vgwrite "@";
+                           next;
+                     }
+    $2 == "Server"   {
+                         vgstatus = vgstatus $1;
+                           next;
+                     }
+    $2 == "Client"   {
+                         vgstatus = vgstatus ",";
+                         vgstatus = vgstatus $1;
+                           next;
+                     }
+    /Max LV/         { maxlv = $3; next;  }
+    /Max PV/         { maxpv = $3; next;  }
+    /Cur LV/         { numlvs = $3; next; }
+    /Cur PV/         { numpvs = $3; next; }
+    /PE Size/        { pesize = $4; next; }
+    /Max PE per PV/  { maxpe = $5; next;  }
+    /Total PVG/      { numpvgs = $3; next; }
+    /LV Name/        {
+                       if (vgprinted == "")
+                       {
+                         printf("VG:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s::::::\n",
+                                 vg, vgstatus, maxlv, maxpv, numlvs,
+                                 numpvs, numpvgs, pesize, maxpe,
+                                 totalpe * pesize, freepe * pesize);
+                         vgprinted = "y";
+                       }
+                       printf("LV:%s:::::::%s::::%s:::::\n",
+                               vg, pesize, $3);
+                       next;
+                     }
+    /Alternate Link$/ {
+                       if (onpvg == "")
+                       {
+                         if (vgprinted == "")
+                         {
+                           printf("VG:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s::::::\n",
+                                   vg, vgstatus, maxlv, maxpv, numlvs,
+                                   numpvs, numpvgs, pesize, maxpe,
+                                   totalpe * pesize, freepe * pesize);
+                           vgprinted = "y";
+                          }
+                          printf("ALT:%s::::::::::::%s:%s:::\n", vg, pv, $3);
+                        }
+                        next;
+                      }
+    /PV Name/         {
+                        pv = $3;
+                        if (onpvg == "")
+                        {
+                          if (vgprinted == "")
+                          {
+                           printf("VG:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s::::::\n",
+                                   vg, vgstatus, maxlv, maxpv, numlvs,
+                                   numpvs, numpvgs, pesize, maxpe,
+                                   totalpe * pesize, freepe * pesize);
+                           vgprinted = "y";
+                          }
+                        }
+                        else
+                        {
+                          printf("PV:%s::::::::::::%s::::%s\n",vg, pv, pvg);
+                          pvg = "";
+                        }
+                        next;
+                      }
+    /Free PE/         {
+                        freepe = $3;
+                        if (freepe_count > 0)
+                        {
+                          printf("PV:%s::::::::::::%s::%s:%s:\n", 
+                                  vg, pv, totalpe, freepe);
+                        }
+                        freepe_count ++;
+                        next;
+                      }
+
+  ' >>   /tmp/sysinfo.vg.tmp
+  mv  /tmp/sysinfo.vg.tmp  $vg_list_file
+#
+# now get list of all logical volumes
+#
+  awk ' BEGIN           { FS = ":" }
+      { type = $1; vgstatus = $2;  pesize = $8;
+        if (type == "LV") 
+        {
+          printf ("%s\n",$13)
+        }
+      }
+  ' $vg_list_file  >> /tmp/sysinfo.lvol.list.tmp
+  sort  /tmp/sysinfo.lvol.list.tmp > $lvol_list_file
+  rm -f /tmp/sysinfo.lvol.list.tmp
+#
+# now get details on each logical volume
+#
+  lvol_list=$(cat $lvol_list_file)
+  for lvol in $lvol_list
+  do
+  
+    lvdisplay $lvol 2>&1 | \
+    awk '
+        BEGIN                  { mwc = 0;
+                                 nummir = 0;
+                                 mircon = 0;
+                                 bbr = 0;
+                                 conalloc = 0 ;
+                                 numstripes = 0;
+                                 stripesize = 0 }
+        /LV Name/              { lvname= $3; next }
+        /LV Permission/        { perm = $3; next }
+        /Mirror copies/        { nummir = $3; next }
+        /Consistency Recovery/ { mwc = $3; next }
+        /Schedule/             { sched = $2; next }
+        /LV Size/              { size = $4; next }
+        /Current LE/           { les = $3; next }
+        /Bad block/            { if ($3 == "on") bbr = 1; badblock = $3;
+                                 if (badblock == "NONE")
+                                   badblock = "NO";
+                                 next }
+        /Allocation/           { alloc=$2;
+                                if (alloc == "non-strict")
+                                   alloc = "ns";
+                                if (alloc == "non-strict/contiguous")
+                                   alloc = "ns-c";
+                                if (alloc == "strict")
+                                   alloc = "s";
+                                if (alloc == "strict/contiguous")
+                                   alloc = "s-c";
+                                if (alloc == "PVG-strict")
+                                   alloc = "PVG";
+                                if (alloc == "PVG-strict/contiguous")
+                                   alloc = "PVG-c";
+                                if (alloc == "PVG-strict/distributed")
+                                   alloc = "PVG-d";
+                                if (alloc == "PVG-strict/partially-distributed")
+                                    alloc = "PVG-p";
+                                  next;
+                               }
+        /LV Status/            { state = $3; 
+                                 if (state == "available/stale")
+                                     state = "stale";
+                                 if (state == "available/syncd")
+                                     state = "syncd";
+                                 if (state == "available")
+                                     state = "avail";
+                                 if (state == "unavailable")
+                                     state = "unavl";
+                                next }
+        /Stripes/              { numstripes = $2; next }
+        /Stripe Size/          { stripesize = $4; next }
+
+                               {next}
+        END                    {
+                                   if ( size == 0 )
+                                   {
+                                       pesize = size;
+                                   }
+                                   else
+                                   {
+                                       pesize = size / les;
+                                   }
+
+                            printf("%-32s %5s %3s %4s %4s %6s %6s  %-5s %-5s\n", 
+                                lvname, size, numstripes,
+                                stripesize, nummir, mwc, state, badblock,alloc)
+                               }
+    ' >> /tmp/sysinfo.lvol.data.tmp
+  done
+  mv /tmp/sysinfo.lvol.data.tmp  $lvol_out_file
+  rm -f /tmp/sysinfo.lvol.data.tmp
+#
+# now get list of all physical volumes
+#
+  awk ' BEGIN           { FS = ":" }
+      { type = $1; vgstatus = $2;  pesize = $8;
+        if (type == "PV") 
+        {
+          printf ("%s\n",$14)
+        }
+      }
+  ' $vg_list_file >> /tmp/sysinfo.pvol.list.tmp
+  sort  /tmp/sysinfo.pvol.list.tmp > $pvol_list_file
+  rm -f /tmp/sysinfo.pvol.list.tmp
+#
+# now get details on each physical volume
+#
+  pvol_list=$(cat $pvol_list_file)
+  for pvol in $pvol_list
+  do
+    pvdisplay -v $pvol 2>&1 | \
+    awk '
+        BEGIN               {found=0}
+
+        /PE Size/           {pesize = $4; next}
+        /LV Name/           {found=1; next}
+        /Physical extents/  {exit 0}
+
+        #
+        # Print "LV_Name Logical_MB_of_LV Physical_MB_of_LV"
+        #
+        {
+            if (found && NF == 3)
+                printf("%s:%d:%d\n", $1, $2 * pesize, $3 * pesize)
+        }
+    ' -  >> $pvol_out_file
+     
+  done
+}  # end of lvm_scan
+
+#===========================================================
+function launch_lvm_scan
+#===========================================================
+{
+  Debug "Building $lvm_scan_command_file"
+
+  rm -f $lvm_scan_done 
+
+  Debug "$vg_out_file, $vg_list_file, $lvol_out_file"
+  Debug "$lvol_list_file, $pvol_list_file $pvol_out_file"
+  Debug "$lvm_scan_done"
+
+  cat <<==end_of_command==   > $lvm_scan_command_file
+
+    # now call the lvm_scan function (exported from parent)
+    lvm_scan
+    date > \$lvm_scan_done
+
+==end_of_command==
+
+  chmod +x $lvm_scan_command_file
+  Debug "  launching $lvm_scan_command_file"
+  $lvm_scan_command_file &
+  Debug "  parent PID = $$, child PID = $!"
+
+}  # end of launch_lvm_scan
+
+#===========================================================
+# launch_kernel_scan
+#===========================================================
+function launch_kernel_scan
+{
+  Debug " Starting launch_kernel_scan.....building $kernel_scan_command_file"
+
+  # change filenames to send output to parent's PID
+  rm -f $kernel_scan_done 
+
+  Debug "$kernel_kinfo $kernel_out_file"
+  Debug "$kernel_scan_done"
+
+  cat <<==end_of_command==   > $kernel_scan_command_file
+
+    # now call the sam_scan function (exported from parent)
+    kernel_scan
+    date > \$kernel_scan_done
+
+==end_of_command==
+
+  chmod +x $kernel_scan_command_file
+  Debug "  launching $kernel_scan_command_file"
+  $kernel_scan_command_file &
+  Debug "  parent PID = $$, child PID = $!"
+
+
+}  # end of launch_kernel_scan
+
+#===========================================================
+# launch_io_scan
+#===========================================================
+function launch_io_scan
+{
+  Debug " Starting launch_io_scan.....building $io_scan_command_file"
+
+  # change filenames to send output to parent's PID
+  rm -f $io_scan_done
+
+  Debug "$io_scan_done"
+
+  if (( ${osmajor} >= 10 ))
+  then
+    cat <<==end_of_command==   > $io_scan_command_file
+
+    # now call the io_scan function (exported from parent)
+      io_scan
+      date > \$io_scan_done
+==end_of_command==
+
+    chmod +x $io_scan_command_file
+    Debug "  launching $io_scan_command_file"
+    $io_scan_command_file &
+    Debug "  parent PID = $$, child PID = $!"
+  else
+    Debug "  on 9.x - executing ioscan"
+    print    "H/W Path      LU       Driver" >> ${io_scan_out}
+    print -- "-----------------------------" >> ${io_scan_out}
+    ioscan -f | tail +3 | \
+      awk '{printf ("%-14s %-6s %-20s\n", $3, $2, $4)}' \
+      >> ${io_scan_out}
+    date > $io_scan_done
+  fi
+
+}  # end of launch_io_scan
+
+
+
+#===========================================================
+# check_if_scan_done
+#===========================================================
+function check_if_scan_done
+{
+  scan_type=$1
+  done_file=/tmp/sysinfo.$$.${scan_type}_done
+  Debug "  starting check_if_scan_done for ${scan_type}."
+  Debug "  looking for /tmp/sysinfo.$$.${scan_type}_done"
+  if (($BATCH))
+  then
+    print -n "retrieving ${scan_type} data "
+  fi
+  if [ ! -f ${done_file} ]     # enter time out loop
+  then
+    Debug "  ${scan_type}_done file not ready, entering loop."
+    FORCE="TRUE"
+    for fil in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+    do
+### Changed 6/15/99 J.Semroc - allow more time for large systems
+###   sleep 5
+      sleep 60
+### end of Change
+      Debug "    in loop $fil"
+      if (($BATCH))
+      then
+        print -n "."
+      fi
+      if [ -f ${done_file} ]
+      then
+        Debug "  ${scan_type} done."
+        FORCE="FALSE"
+        break
+      fi
+    done
+    if [ "$FORCE" = "TRUE" ]
+    then
+      Debug "  still no file, forcing an interactive ${scan_type}."
+      ${scan_type}
+    fi
+  else
+    Debug "  found ${scan_type}_done file"
+    if (($BATCH))
+    then
+      print ""
+    fi
+  fi
+}
+
+#===========================================================
+# io_scan
+#
+# this routine runs ioscan with the appropriate parameters and
+# places the output in /var/sam/.iout   It also
+# provides a semaphore for other asyncronous processes, by removing
+# and then creating /var/sam/.dion
+#
+#===========================================================
+function io_scan
+{
+
+#       bus_type               = $1
+#       cdio                   = $2
+#       is_block               = $3
+#       is_char                = $4
+#       is_pseduo              = $5
+#       block_major_number     = $6
+#       character_major_number = $7
+#       minor_number           = $8
+#       class                  = $9
+#       low_level_driver       = $10
+#       full_hw                = $11
+#       identify_bytes         = $12
+#       device instance        = $13
+#       module_path            = $14
+#       driver                 = $15
+#       sw_status              = $16
+#       hardware_type          = $17
+#       ident                  = $18
+#       instance               = $19
+
+#
+#  If an inadaquate description is given from ioscan.  Initiate a diskinfo
+#  request to force an open operation and then perform another ioscan for
+#  that device.  If the diskinfo operation results in an error containing
+#  "Permission Denied" the device may be a tape drive needing a slightly
+#  different device file.  At the time of this modification to this script
+#  (7/5/94) an HPFL Disc will only give a useful description when there is
+#  a mounted file system on the disk.
+#
+#  Change the Internal Field Separator to ":".  All output fields of
+#  ioscan -F are separated by a ":".
+#
+  LANG=$save_lang
+  LANG=C
+  rm -f $io_scan_done
+  ioscan $ioscan_args > $io_scan_tmp
+  mv $io_scan_out $io_scan_out.old > /dev/null 2>&1
+  ifs_save=$IFS
+  IFS=:
+  cat $io_scan_tmp | \
+  while read p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 p16 p17 p18 p19
+  do
+      if [ "$p18" = "HP-IB Device" -o "$p18" = "HP-IB Tape" -o \
+           "$p18" = "SCSI Tape"    -o "$p18" = "HPFL Disc" ]
+      then
+          mksf -r -H $p11 /var/sam/sam_tmp >/dev/null 2>&1
+          diskinfo /var/sam/sam_tmp >/dev/null 2>/var/sam/sam_err
+          if ( grep Permission /var/sam/sam_err >/dev/null 2>&1 )
+          then
+              mksf -r -c -u 0 -H $p11 /var/sam/sam_tmp >/dev/null 2>&1
+              diskinfo /var/sam/sam_tmp >/dev/null 2>&1
+          fi
+          ioscan -H $p11 -F >>$io_scan_out 2>/dev/null
+          rm -f /var/sam/sam_tmp /var/sam/sam_err
+      else
+          echo "$p1:$p2:$p3:$p4:$p5:$p6:$p7:$p8:$p9:$p10:$p11:$p12:$p13:$p14:$p15:$p16:$p17:$p18:$p19" >>$io_scan_out
+      fi
+  done
+  IFS=$ifs_save
+  date > $io_scan_done
+  rm $io_scan_tmp
+
+} #end of io_scan
+
+#==================================================================
+# sw_scan
+#==================================================================
+function sw_scan
+{
+  Debug "Beginning SW_SCAN"
+  if [[ -f /usr/sbin/swlist ]]
+  then
+    Debug "  found swlist"
+    if (($HTML))
+    then
+      print "
" >> /tmp/sysinfo.$$.swout + print "" >> /tmp/sysinfo.$$.swout + print "

" >> /tmp/sysinfo.$$.swout + print "

TOP

" >> /tmp/sysinfo.$$.swout + print "" >> /tmp/sysinfo.$$.swout + print "Software Data

" >> /tmp/sysinfo.$$.swout + print "
"  >> /tmp/sysinfo.$$.swout
+    else
+      print "" >> /tmp/sysinfo.$$.swout
+      print "SOFTWARE DATA" >> /tmp/sysinfo.$$.swout
+      print "=============" >> /tmp/sysinfo.$$.swout
+    fi
+  if (($BATCH))
+  then
+    print  "collecting software data "
+  fi
+    print    "Product                Version         Description" >> /tmp/sysinfo.$$.swout
+    print -- "--------------------------------------------------" >> /tmp/sysinfo.$$.swout
+    swlist -l product | grep -v "^#" | expand >> /tmp/sysinfo.$$.swout
+    f_display_file /tmp/sysinfo.$$.swout
+    #check for non-configured software
+    print  "checking for incompletely installed software "
+    swlist -l fileset -a state | grep -e transient -e corrupt \
+            -e available -e installed | expand >> /tmp/sysinfo.$$.sw_not_conf
+    if [[ -s /tmp/sysinfo.$$.sw_not_conf ]]
+    then
+      print "" >> /tmp/sysinfo.$$.sw_not_header
+      print "Found incompletely installed filesets" >> \
+                /tmp/sysinfo.$$.sw_not_header
+      print "=====================================" >> \
+                /tmp/sysinfo.$$.sw_not_header
+      print "WARNING (${sysname}): Found incompletely installed software." >> \
+             /tmp/sysinfo.$$.errwarn
+      f_display_file /tmp/sysinfo.$$.sw_not_header
+      f_display_file /tmp/sysinfo.$$.sw_not_conf
+    fi
+  else
+  Debug " could not find swlist."
+  fi
+}
+
+#==================================================================
+#f_build_sched_file
+#        This function retrieves the os version information and
+#        series type. It then sets the kernel pointers.
+#===================================================================
+function f_build_sched_file
+{
+  Debug "Beginning build_sched_file"
+cat <<==end_of_file==   >  ${where}/sched.models
+600     1.0     PA7000
+635     1.0     PA7000
+645     1.0     PA7000
+700     1.1     PA7000
+705     1.1a    PA7000
+715     1.1c    PA7100LC
+710     1.1a    PA7000
+712     1.1c    PA7100LC
+720     1.1a    PA7000
+722     1.1c    PA7100LC
+725     1.1c    PA7100LC
+728     1.1d    PA7200
+730     1.1a    PA7000
+735     1.1b    PA7100
+742     1.1b    PA7100
+743     1.1c    PA7100LC
+744     1.1e    PA7300
+745     1.1b    PA7100
+747     1.1b    PA7100
+750     1.1a    PA7000
+755     1.1b    PA7100
+770     1.1d    PA7200
+777     1.1d    PA7200
+778     1.1e    PA7300
+779     1.1e    PA7300
+780     2.0     PA8000
+781     2.0     PA8000
+782     2.0     PA8200
+785     2.0     PA8500
+800     1.0     PA7000
+801     1.1c    PA7100LC
+802     2.0     PA8000
+803     1.1e    PA7300
+804     2.0     PA8000
+806     1.1c    PA7100LC
+807     1.1a    PA7000
+808     1.0     PA7000
+809     1.1d    PA7200
+810     2.0     PA8000
+811     1.1c    PA7100LC
+813     1.1e    PA7300
+815     1.0     PA7000
+816     1.1c    PA7100LC
+817     1.1a    PA7000
+819     1.1d    PA7200
+820     2.0     PA8000
+821     1.1d    PA7200
+822     1.0     PA7000
+825     1.0     PA7000
+826     1.1c    PA7100LC
+827     1.1a    PA7000
+829     1.1d    PA7200
+831     1.1d    PA7200
+832     1.0     PA7000
+834     1.0     PA7000
+835     1.0     PA7000
+837     1.1a    PA7000
+839     1.1d    PA7200
+840     1.0     PA7000
+841     1.1d    PA7200
+842     1.0     PA7000
+845     1.0     PA7000
+847     1.1a    PA7000
+849     1.1d    PA7200
+850     1.0     PA7000
+851     1.1d    PA7200
+852     1.0     PA7000
+855     1.0     PA7000
+856     1.1c    PA7100LC
+857     1.1a    PA7000
+859     1.1d    PA7200
+860     1.0     PA7000
+861     2.0     PA8000
+865     1.0     PA7000
+867     1.1a    PA7000
+869     1.1d    PA7200
+870     1.0     PA7000
+871     2.0     PA8000
+877     1.1a    PA7000
+879     2.0     PA8000
+887     1.1b    PA7100
+889     2.0     PA8000
+890     1.0     PA7000
+891     1.1b    PA7100
+892     1.1b    PA7100
+893     2.0     PA8000
+897     1.1b    PA7100
+898     2.0     PA8200
+899     2.0     PA8200
+F10     1.1a    PA7000
+F20     1.1a    PA7000
+H20     1.1a    PA7000
+F30     1.1a    PA7000
+G30     1.1a    PA7000
+H30     1.1a    PA7000
+I30     1.1a    PA7000
+G40     1.1a    PA7000
+H40     1.1a    PA7000
+I40     1.1a    PA7000
+G50     1.1b    PA7100
+H50     1.1b    PA7100
+I50     1.1b    PA7100
+G60     1.1b    PA7100
+H60     1.1b    PA7100
+I60     1.1b    PA7100
+G70     1.1b    PA7100
+H70     1.1b    PA7100
+I70     1.1b    PA7100
+E25     1.1c    PA7100LC
+E35     1.1c    PA7100LC
+E45     1.1c    PA7100LC
+E55     1.1c    PA7100LC
+T500    1.1b    PA7100
+T520    1.1b    PA7100
+T540    2.0     PA8000
+T600    2.0     PA8000
+K100    1.1d    PA7200
+K200    1.1d    PA7200
+K210    1.1d    PA7200
+K220    1.1d    PA7200
+K230    1.1d    PA7200
+K400    1.1d    PA7200
+K410    1.1d    PA7200
+K420    1.1d    PA7200
+DXO     1.1c    PA7100LC
+DX0     1.1c    PA7100LC
+DX5     1.1c    PA7100LC
+D200    1.1c    PA7100LC
+D210    1.1c    PA7100LC
+D310    1.1c    PA7100LC
+D410    1.1d    PA7200
+D250    1.1d    PA7200
+D350    1.1d    PA7200
+J200    1.1d    PA7200
+J210    1.1d    PA7200
+C100    1.1d    PA7200
+J220    2.0     PA8000
+J280    2.0     PA8000
+J282    2.0     PA8000
+S715    1.1e    PA7300
+S760    1.1e    PA7300
+D650    2.0     PA8000
+J410    2.0     PA8000
+J400    2.0     PA8000
+J210XC  1.1d    PA7200
+J2240   2.0     PA8200
+J5000   2.0     PA8500
+J5600   2.0     PA8600
+J6000   2.0     PA8600
+J7000   2.0     PA8500
+C200+   2.0     PA8200
+C240+   2.0     PA8200
+C360    2.0     PA8500
+C180    2.0     PA8000
+C180-XP 2.0     PA8000
+C160    2.0     PA8000
+C160L   1.1e    PA7300
+C140    2.0     PA8000
+C130    2.0     PA8000
+C120    1.1e    PA7300
+C115    1.1e    PA7300
+C110    1.1d    PA7200
+B160L   1.1e    PA7300
+B132L   1.1e    PA7300
+B120    1.1e    PA7300
+B115    1.1e    PA7300
+B1000   2.0     PA8500
+B2000   2.0     PA8500
+C3000   2.0     PA8500
+C3600   2.0     PA8600
+S700i   1.1e    PA7300
+S744    1.1e    PA7300
+D330    1.1e    PA7300
+D230    1.1e    PA7300
+D320    1.1e    PA7300
+D220    1.1e    PA7300
+D360    1.1d    PA7200
+K360    2.0     PA8000
+K370    2.0     PA8200
+K460    2.0     PA8000
+K460-EG 2.0     PA8000
+K460-XP 2.0     PA8000
+K260    2.0     PA8000
+K260-EG 2.0     PA8000
+D260    1.1d    PA7200
+D270    2.0     PA8000
+D280    2.0     PA8000
+D370    2.0     PA8000
+D380    2.0     PA8000
+D390    2.0     PA8000
+R380    2.0     PA8000
+R390    2.0     PA8000
+K250    2.0     PA8000
+K450    2.0     PA8000
+K270    2.0     PA8200
+K470    2.0     PA8200
+K380    2.0     PA8200
+K580    2.0     PA8200
+V2200   2.0     PA8200
+V2250   2.0     PA8200
+V2500   2.0     PA8500
+V2600   2.0     PA8600
+L1000-36  2.0   PA8500
+L1000-44  2.0   PA8500
+L2000-36  2.0   PA8500
+L2000-44  2.0   PA8500
+N4000-36  2.0   PA8500
+N4000-44  2.0   PA8500
+A180    1.1     PA7300LC
+A180C   1.1     PA7300LC
+
+==end_of_file==
+
+chmod 444 ${where}/sched.models
+}
+
+#==================================================================
+#f_get_sys_type
+#        This function retrieves the os version information and
+#        series type. It then sets the kernel pointers.
+#===================================================================
+function f_get_sys_type
+{
+  Debug "Beginning GET SYSTEM TYPE"
+  osletter=$(uname -r | awk -F. '{print $1}')
+  osmajor=$(uname -r | awk -F. '{print $2}')
+  osminor=$(uname -r | awk -F. '{print $3}')
+  system=$(uname -m | awk -F/ '{print $1}')
+  series=$(uname -m | awk -F/ '{print $2}')
+  machine_id=$(uname -i)
+  license=$(uname -l)
+  Debug "  `uname -a`"
+  Debug "  system = $system"
+  Debug "  series = $series"
+  Debug "  OS = $osletter.$osmajor.$osminor"
+  Debug "  machine = $machine_id"
+  Debug "  license = $license"
+  if (( `echo $series | cut -b1` == 7 )) && (( $osmajor <= 9 ))
+  then
+    print "\nSorry. Not supported on Series 700 running HP-UX 9.x\n"
+    exit ${ERROR}
+  fi
+  if [[ ! -z $(whence model) ]]
+  then
+    series=`model`
+    Debug "  model returned $series"
+    sched_file=${where}/sched.models
+    f_build_sched_file
+    pa_ver="N/A"
+    pa_chip="N/A"
+    if [[ -f ${sched_file} ]]
+    then
+      check_pa=$(model | awk -F/ '{print $NF}')
+      pa_ver=$(grep "${check_pa} " $sched_file | awk '{print $2}')
+      pa_chip=$(grep "${check_pa} " $sched_file | awk '{print $3}')
+    else
+      pa_ver=""
+      pa_chip=""
+    fi
+  else
+    Debug "  model not found, using inline series check"
+    case $series in
+      780) series="780/C160";;
+      801) series="801/DX0";;
+      811) series="811/DX5";;
+      806) series="806/E25";;
+      807) series="807/F10";;
+      809) series="809/K100";;
+      816) series="816/E35";;
+      817) series="817/F20";;
+      819) series="819/K200";;
+      821) series="821/D200/D400";;
+      826) series="826/E45";;
+      827) series="827/H20";;
+      829) series="829/K400";;
+      831) series="831/D200/D400";;
+      837) series="837/F30";;
+      839) series="839/K210";;
+      841) series="841/D210/D410";;
+      847) series="847/G30/H30";;
+      851) series="851/D210/D410";;
+      849) series="849/K410";;
+      856) series="856/E55";;
+      857) series="857/I30";;
+      859) series="859/K220";;
+      867) series="867/G40/H40";;
+      869) series="869/K420";;
+      877) series="877/I40";;
+      887) series="887/G50/H50/G60/H60/G70/H70";;
+      890) series="890";;
+      891) series="891/T500";;
+      892) series="892/T520";;
+      897) series="897/I50/I60/I70";;
+        *) series=$series
+             Debug "Series type not found for $series" ;;
+    esac
+  fi
+  if [ $osmajor -lt 10 ]
+  then
+    kernel=/hp-ux
+  else
+    kernel=/stand/vmunix
+  fi
+  Debug "  kernel=${kernel}"
+  if [[ -f /etc/lvmtab ]]
+  then
+    lvm_installed=1
+    Debug "  LVM Detected."
+  else
+    lvm_installed=0
+    Debug "  LVM NOT Detected."
+  fi
+# set PATH variable to ensure access to required commands
+  for i in \
+    /etc \
+    /usr/sbin \
+    /bin \
+  ; 
+  do
+    if [ -d $i ]
+    then
+      PATH=$PATH:$i
+    fi
+  done
+  export PATH
+  Debug "  PATH = $PATH"
+  if [ -f ${where}/system_db ]
+  then
+    conf_file=${where}/system_db
+  elif [ -f /usr/local/bin/system_db ]
+  then
+    conf_file=/usr/local/bin/system_db
+  fi
+  if [[ -n ${conf_file} ]]
+  then
+    Debug "  Found config file = $conf_file"
+    serial_number=$(grep $sysname $conf_file | awk -F: '{print $4}')
+    Debug "    serial number = ${serial_number}"
+  fi
+  swap_devs=$(swapinfo -d | grep -v "Kb" |grep -v "TYPE" \
+         | awk '{printf "%s ", $NF}')
+# if (($FULL_KERNEL)) || (( ${LITE_KERNEL} ))
+# then
+#   if (( ${osmajor} >= 10 ))
+#     rm -f $kernel_scan_done
+#     launch_kernel_scan
+#     Debug "Calling kernel_scan"
+#     if (($BATCH))
+#     then
+#       print "launching background kernel scan"
+#     fi
+#   fi
+# fi
+  if (($IOSCAN)) || (($PHYSICAL))
+  then
+    rm -f $io_scan_done
+    launch_io_scan $ioscan_args
+    Debug "Calling io_scan"
+    if (($BATCH))
+    then
+      print "launching background ioscan"
+    fi
+  fi
+  if (($LVMSCAN))
+  then
+    rm -f $lvm_scan_done
+    launch_lvm_scan
+    Debug "Calling launch_lvm_scan"
+    if (($BATCH))
+    then
+      print "launching background lvm scan"
+    fi
+  fi
+}                # end of f_get_sys_type
+
+#===================================================================
+#
+#===================================================================
+function f_run_cstm
+{
+cstm_ok=0
+print -n "Checking digmond...."
+
+diagmond_result=$(ps -ef | grep -v grep | grep  diagmond > /dev/null 2>&1)
+diagmond_result=$?
+if (($diagmond_result != 0))
+then
+  cstm_ok=1
+  Debug "Error: diagmond is not running."
+  Debug "diagmond_result = $diagmond_result"
+  print "diagmond is NOT running!"
+  print "No cstm info is available."
+  return
+else
+  print "diagmond is running."
+fi
+
+print -n "Checking cstm......."
+cstm_version=$(echo "Version" | /usr/sbin/cstm | grep Version \
+          | tail -1 |  awk '{print $2}' )
+cstm_major=$(echo ${cstm_version} | awk -F. '{print $2}')
+
+echo "Version" | /usr/sbin/cstm >/dev/null 2>&1
+cstm_result=$?
+if (($cstm_result != 0)) 
+then
+  cstm_ok=1
+  Debug "Error returned from cstm"
+  print "Error returned from cstm"
+  print "cstm_result = $cstm_result"
+fi
+
+if (($cstm_ok == 0))
+then
+  cstm_version=$(echo "Version" | /usr/sbin/cstm | grep Version \
+            | tail -1 |  awk '{print $2}' )
+  cstm_major=$(echo ${cstm_version} | awk -F. '{print $2}')
+  print "found version ${cstm_version}"
+  ### Changed 6/30/99 J.Semroc also works with V14 
+  ### if (($cstm_major >= 08))  && (($cstm_major <= 13))
+  ###  and V16 
+  ### if (($cstm_major >= 08))  && (($cstm_major <= 16))
+  ### Removed upper end version check on cstm 
+  if (($cstm_major >= 08))
+  ### end Change
+  then
+    print "Running cstm....please wait."
+  
+    if (($cstm_major == 12))
+    then
+      cat <<==end_of_command==   > ${cstm_command_file}
+      # testtt
+      #
+      # The following connects the UI to the system the UI is being run on,
+      #  and selects this system.
+      # SelCurrentSys system add localhost  system 1
+      # Use current user name
+      # No password required on local system.
+      # don't prompt for overwrite
+      gop confirmation no
+      #Select all
+      #Select class cpu
+      SelClass type processor qualifier cpu
+      #collect the information and wait for it to finish
+      Information ; wait
+      #now display the info log
+      #version 12 has SaveAs & Print Commands reversed.
+      InfoLog
+        Print
+        $cstm_cpu_out
+        Done
+      #map
+      unselall
+      SelClass type memory
+      Information ; wait
+      InfoLog
+        Print
+        $cstm_mem_out
+        Done
+      unselall
+      SelClass type disk
+      Information ; wait
+      InfoLog
+        Print
+        $cstm_disk_out
+        Done
+      Exit
+      Ok
+  
+==end_of_command==
+  
+    else
+
+      cat <<==end_of_command==   > ${cstm_command_file}
+      # testtt
+      #
+      # The following connects the UI to the system the UI is being run on,
+      #  and selects this system.
+      #SelCurrentSys system add localhost  system 1
+      # Use current user name
+      # No password required on local system.
+      #don't prompt for overwrite
+      gop confirmation no
+      #Select all
+      #Select class cpu
+      SelClass type processor qualifier cpu
+      #collect the information and wait for it to finish
+      Information ; wait
+      #now display the info log
+      InfoLog
+        SaveAs
+        $cstm_cpu_out
+        Done
+      #map
+      unselall
+      SelClass type memory
+      Information ; wait
+      InfoLog
+        SaveAs
+        $cstm_mem_out
+        Done
+      unselall
+      SelClass type disk
+      Information ; wait
+      InfoLog
+        SaveAs
+        $cstm_disk_out
+        Done
+      Exit
+      Ok
+  
+==end_of_command==
+    fi
+    /usr/sbin/cstm -f ${cstm_command_file} > /dev/null 2>&1
+
+  awk '
+      /Hardware path/         {path = $3; next;}
+      /Slot Number/           {slot = $3; next; }
+      /PDC Firmware Revision/ {pdc = $4; next;}
+      /Instruction Cache/     {icache = $4; next;}
+      /Data Cache/            {dcache = $4; next;}
+      /Instruction TLB/       {itlb = $4; next;}
+      /Data TLB/              {dtlb = $5;
+                               printf("%6s%6s  %12s%8s %6s%6s %6s\n",
+                               path, slot, pdc, icache, dcache, itlb, dtlb);
+                              }
+      ' $cstm_cpu_out > $cstm_cpu_out2
+
+  rm -f ${cstm_command_file}
+  #rm -f ${cstm_cpu_out}
+  Debug "cstm_cpu_out2 = ${cstm_cpu_out2}"
+  #Debug "Data Cache            = ${data_cache}"
+  #Debug "Instruction Cache     = ${inst_cache}"
+  #Debug "Data TLB              = ${data_cache}"
+  #Debug "Instruction TLB       = ${inst_cache}"
+  #Debug "PDC Firmware Revision = ${pdc_rev}"
+  #Debug "${memory_interleave}"
+
+    check_na=$(awk ' /Total Physical Memory/   {print $NF}
+            ' $cstm_mem_out  | tail -1)
+    if [[ ${check_na} != "N/A" ]]
+    then
+      cstm_tot_phys=$(awk ' /Total Physical Memory/   {print $(NF -1)}
+        ' $cstm_mem_out  | tail -1)
+    else
+      cstm_tot_phys="N/A"
+    fi
+    cstm_tot_conf=$(awk ' /Total Configured Memory/  {print $(NF -1)}
+        ' $cstm_mem_out  | tail -1)
+    cstm_page_size=$(awk ' /Page Size/ {print $(NF -1)}
+        ' $cstm_mem_out  | tail -1)
+    cstm_interleave=$(awk ' /Memory interleaving/ { print $0}
+        ' $cstm_mem_out | tail -1)
+    if [[ -z ${cstm_tot_conf} ]] || [[ -z ${cstm_page_size} ]]
+    then
+      cstm_tot_conf="N/A"
+      cstm_page_size="N/A"
+    fi
+  else
+    print "Sorry, this version is not supported by SysInfo."
+    print "Hardware level information will not be available."
+    Debug "unsupported cstm version ${cstm_version}"
+    cstm_ok=1
+  fi
+fi
+} # end of function f_run_cstm
+
+
+#===================================================================
+# f_get_array_data
+#===================================================================
+function f_get_array_data
+{
+  if (( ${cstm_ok} == 0 ))
+  then
+    Debug "Getting array data"
+    if (($cstm_major == 12))
+    then
+      cat <<==end_of_command==   > ${cstm_command_file}
+      # testtt
+      #
+      # The following connects the UI to the system the UI is being run on,
+      #  and selects this system.
+      # SelCurrentSys system add localhost  system 1
+      # Use current user name
+      # No password required on local system.
+      # don't prompt for overwrite
+      gop confirmation no
+      #unselall
+      Sel path $hwpath
+      Information ; wait
+      InfoLog
+        Print
+        $cstm_array_out
+        Done
+      Exit
+      Ok
+
+==end_of_command==
+
+    else   # cstm not equal to 12
+
+      cat <<==end_of_command==   > ${cstm_command_file}
+      # testtt
+      #
+      # The following connects the UI to the system the UI is being run on,
+      #  and selects this system.
+      #SelCurrentSys system add localhost  system 1
+      # Use current user name
+      # No password required on local system.
+      #don't prompt for overwrite
+      gop confirmation no
+      #unselall
+      Sel path $hwpath
+      Information ; wait
+      InfoLog
+        SaveAs
+        $cstm_array_out
+        Done
+      Exit
+      Ok
+
+==end_of_command==
+
+    fi
+    /usr/sbin/cstm -f ${cstm_command_file} > /dev/null 2>&1
+    cat $cstm_array_out | tail +6 >> /tmp/sysinfo.$$.array
+    rm $cstm_array_out
+  else
+    print "Array info not available  -  Problem with cstm."
+  fi
+
+} # end of f_get_array_data
+
+#===================================================================
+# f_get_system_data
+#        This function retrieves various system  and boot data.
+#        e.g. amount of memory, number of cpus, etc.
+#        It also checks to see if memory dumping is properly
+#        configured.
+#===================================================================
+function f_get_system_data
+{
+  if [[ (-f /usr/sbin/cstm) && (-x /usr/sbin/cstm) ]]
+  then
+    f_run_cstm 
+  else
+    cstm_ok=1
+    Debug " cstm not found....skipping"
+    print "WARNING (${sysname}): CSTM not installed." >> \
+           /tmp/sysinfo.$$.errwarn
+  fi
+  Debug "Beginning SYSTEM/ROOT check."
+  if (($BATCH))
+  then
+    print -n "collecting system data "
+  fi
+  # Determine cpu speed in MHz
+  # Determine the memory size.
+  # Real memory expressed in units of pages (4 kbytes per page).
+  if [ ${osmajor} -eq 11 ]
+  then
+  cpu_speed=$(echo itick_per_tick/D | adb -k $kernel /dev/kmem | tail -1 \
+          | awk '{print $2 / 10000}')
+  REAL_MEM=$(echo 'phys_mem_pages/D'| adb -k $kernel /dev/kmem | tail -1 \
+             | awk '{print $2}')
+  MemoryDumpSize=$(expr ${REAL_MEM} / 256)
+  k32_64=$(getconf KERNEL_BITS)
+  processor_count=$(echo 'processor_count/D' | adb -k $kernel /dev/kmem \
+                    | tail -1 | awk '{print $2}')
+  else
+  cpu_speed=$(echo itick_per_tick/D | adb $kernel /dev/kmem | tail -1 \
+          | awk '{print $2 / 10000}')
+  dumppages=$(echo 'dumpsize/D'| adb  $kernel /dev/kmem | tail -1 \
+              | awk '{print $2}')
+  MemoryDumpSize=$(expr ${dumppages} / 256)
+  REAL_MEM=$(echo 'physmem/D'| adb  $kernel /dev/kmem | tail -1 \
+             | awk '{print $2}')
+  # determine number of active processors (cpus)
+  processor_count=$(echo 'processor_count/D' | adb $kernel /dev/kmem \
+                    | tail -1 | awk '{print $2}')
+  fi
+  memory=$(expr ${REAL_MEM} / 256)
+  if (($BATCH))
+  then
+    print -n "."
+  fi
+  boot_time=$(who -b | awk '{printf "%s %s %s ", $4, $5, $6}')
+   
+  #now get date of last patch
+  #
+  #Modified: Greg Sterling
+  #updated lastpatch for 10.x & 11.x
+  #
+  last_patch="unknown"
+  if (( $osmajor >= 10 ))
+  then
+    if [ -d /var/adm/sw/products ]
+    then
+      last_patch=$(ls -lt /var/adm/sw/products | egrep 'PHNE|PHCO|PHKL|PHSS' \
+           | head -1 | awk '{ print $6,$7,$8}')
+    else
+      Debug "ERROR: Product IPD database does not exist."
+    fi
+  else
+    if [ -d /system ]
+    then
+      last_patch=$(ls -lt /system/ | egrep 'PHNE|PHCO|PHKL|PHSS' \
+           | head -1 | awk '{ print $6,$7,$8}')
+    fi
+  fi
+  if (($BATCH))
+  then
+    print -n "."
+  fi
+  if (($BATCH))
+  then
+    print -n "."
+  fi
+  lvlnboot -v /dev/vg00 > /tmp/sysinfo.$$.root 2>&1
+  rootboot=$(grep "Boot Disk" /tmp/sysinfo.$$.root \
+             | awk '{printf "%s  ", $1}')
+  rootroot=$(grep "^Root:" /tmp/sysinfo.$$.root \
+             | awk '{print $2,$4}')
+  rootswap=$(grep "^Swap:" /tmp/sysinfo.$$.root \
+             | awk '{print $2,$4}')
+  rootdump=$(grep "^Dump:" /tmp/sysinfo.$$.root \
+             | awk '{printf  "%s ",$2}')
+  if (($BATCH))
+  then
+    print -n "."
+  fi
+
+  kernel_size=$(ll ${kernel} | awk '{print $5}' 2>&1)
+# gather memory dump parms
+  (( KernelSizeMb=`ll ${kernel} | awk '{print $5}'` / 1024 / 1024 + 1 ))
+  if (( $osmajor < 10 ))
+  then                                        
+    # it is a 9.x system
+    if [[ $(grep "dumps" /etc/conf/gen/S800*) = *default* ]]
+    then
+      DumpDiskCapacity=$(swapinfo -mat | grep dev | head -1 | awk '{print $2}')
+      rootdump="default"
+      Debug "  9.x system. default found in S800."
+      Debug "  rootdump=${rootdump}"
+    else
+      Debug "  9.x system. default not found in S800. using rootdump"
+    fi
+    SAVECORE=0
+    grep "/etc/savecore" /etc/rc > /dev/null 2>&1
+    if ((! ($?) ))
+    then
+      SAVECORE=1
+    fi
+    SAVECORE_DIR=$(grep "/etc/savecore" /etc/rc | tail -1 | awk '{print $NF}')
+
+  elif  (( $osmajor >= 11 ))
+  then
+    # 11.x system
+    Debug "  11.x system."
+    if [ -f /etc/rc.config.d/savecrash ] 
+    then
+      . /etc/rc.config.d/savecrash
+      # check if SAVECRASH is set
+      if [[ ! -z $SAVECRASH ]]
+      then
+      case ${COMPRESS} in
+        0) Compress_Option="Turned off. ";;
+        1) Compress_Option="Turned on. ";;
+        2) Compress_Option="Don't care (default). ";;
+        *) Compress_Option="N/A";;
+      esac
+      SAVECORE=$SAVECRASH
+      SAVECORE_DIR=$SAVECRASH_DIR
+      Debug "  found /etc/rc.config.d/savecrash with following parms:"
+      Debug "    SAVECORE=$SAVECRASH"
+      Debug "    SAVECORE_DIR=$SAVECRASH_DIR"
+      Debug "    CHUNK_SIZE=$CHUNK_SIZE"
+      Debug "    COMPRESS=$COMPRESS"
+      Debug "    MIN_FREE=$MIN_FREE"
+      Debug "    SWAP_LEVEL=$SWAP_LEVEL"
+      Debug "    FOREGRD=$FOREGRD"
+      Debug "    SAVE_PART=$SAVE_PART"
+      Debug "    LOG_ONLY=$LOG_ONLY"
+      else
+      Debug "  SAVECRASH is DISABLED"
+      SAVECORE=0
+      fi
+#
+# Now check for the crashconf file. This file can be used to set additional
+# dump parameters in V11.x
+#
+      if [ -f /etc/rc.config.d/crashconf ]
+      then
+        . /etc/rc.config.d/crashconf
+        # check if CRASHCONF_ENABLED is set
+        if [[ -z $CRASHCONF_ENABLED ]]
+        then
+          Debug "  CrashConf is disabled."
+      else
+
+# Changed: 12/15/98 by Greg Sterling
+# If we're using V11.x, and CRASHCONF is enabled, the we've costomized the
+# DUMP output. The equation below does not apply to this scenario.
+# TotalMemoryDumpSize = MemoryDumpSize + KernelSizeMb + KernelSafetyFactor
+#
+# The actual dumpsize can not be computed from the crashconf utility. The
+# kernel size and safety values can be set to zero since the crashconf utility
+# already calculates these values.
+#
+        crashconf_dumpsize=$(/sbin/crashconf -v | grep 'Total pages included' \
+               | awk '{ print $6}')
+        crashconf_dumpsize=$(expr ${crashconf_dumpsize} \* 4096 / 1024 / 1024)
+#       KernelSizeMB=0
+#       KernelSafetyFactor=0
+
+        Debug "  found /etc/rc.config.d/crashconf with following parms:"
+        Debug "    CRASHCONF_ENABLED=$CRASHCONF_ENABLED"
+        Debug "    CRASH_INCLUDED_PAGES=$CRASH_INCLUDED_PAGES"
+        Debug "    CRASH_EXCLUDED_PAGES=$CRASH_EXCLUDED_PAGES"
+        Debug "    CRASHCONF_READ_FSTAB=$CRASHCONF_READ_FSTAB"
+        Debug "    CRASHCONF_REPLACE=$CRASHCONF_REPLACE"
+#
+# Check to see if the CRASHCONF_READ_FSTAB variable is set. If yes, then
+# the system will check the /etc/fstab file to see if there are any additional
+# dump spaces defined. If the CRASHCONF_REPLACE variable is set then all
+# the definitions/parameters defined in this section will replace any
+# previously defined DUMP parameters in the kernel.
+#
+        if [[ ! -z $CRASHCONF_READ_FSTAB ]]
+        then
+          Debug "  crashconf_read_fstab is enabled."
+          crashconf_fstabs=$(awk ' $3=="dump" {printf("%s ",$1)}' /etc/fstab )
+
+          Debug "  list of crashconf_fstabs = $crashconf_fstabs"
+          if [[ -z $crashconf_fstabs ]]
+          then
+            Debug "  crashconf_fstabs list is empty. Will continue to "
+            Debug "    use the lvlnboot info."
+          else
+            if [[ -z $CRASHCONF_REPLACE ]]
+            then
+              Debug "  crashconf_fstabs will be added to existing \
+                         configuration."
+              rootdump="$rootdump $crashconf_fstabs"
+            else
+              Debug "  crashconf_fstabs will replace default dump config."
+              rootdump=$crashconf_fstabs
+            fi
+          fi
+        fi
+      fi
+    else
+      Debug "  could not find /etc/rc.config.d/crashconf"
+      print "ERROR ${sysname}:/etc/rc.config.d/crashconf defaults file MISSING" >> /tmp/sysinfo.$$.errwarn
+    fi
+  fi
+  else  
+    # must be a 10.x system
+    Debug "  10.x system."
+    if [ -f /etc/rc.config.d/savecore ] 
+    then
+      . /etc/rc.config.d/savecore
+      case ${COMPRESS} in
+        0) Compress_Option="Turned off. ";;
+        1) Compress_Option="Turned on. ";;
+        2) Compress_Option="Don't care (default). ";;
+        *) Compress_Option="N/A";;
+      esac
+      Debug "  found /etc/rc.config.d/savecore with following parms:"
+      Debug "    SAVECORE=$SAVECORE"
+      Debug "    SAVECORE_DIR=$SAVECORE_DIR"
+      Debug "    CHUNK_SIZE=$CHUNK_SIZE"
+      Debug "    COMPRESS=$COMPRESS"
+      Debug "    MIN_FREE=$MIN_FREE"
+      Debug "    SWAP_LEVEL=$SWAP_LEVEL"
+      Debug "    FOREGRD=$FOREGRD"
+    else
+      Debug "  could not find /etc/rc.config.d/savecore"
+      print "ERROR: ${sysname}:/etc/rc.config.d/savecore defaults file MISSING" >> /tmp/sysinfo.$$.errwarn
+    fi
+
+  fi
+
+# Updated: Greg Sterling
+# I moved this section of code outside the savesore/crashconf checks above.
+# This dump calculation is pertinent to any version of HPUX and should not be
+# restricted to V10.x
+#
+  if (($lvm_installed))
+  then
+    Debug "Checking for dump space"
+    Debug "  rootdump = ${rootdump}"
+    for logvol in ${rootdump}
+    do
+#
+# Added by Greg Sterling
+# This code checks for a preceeding /dev. if it exists then
+# continue, otherwise append the /dev/vg00 to the lvol definition.
+#
+      echo $logvol | grep -q "^/dev/"
+      if [[ $? -ne 0 ]]
+      then
+        Debug "  adding /dev/vg00 to lvol name"
+        logvol="/dev/vg00/${logvol}"
+      fi
+#
+# Added by Greg Sterling
+# Validate the logvol is a logical volume. Its possible for dump
+# devices in later versions of HPUX to be disks w/out LVM
+#
+        lvdisplay $logvol > /dev/null 2>&1
+        if [[ $? -ne 0 ]]
+        then
+          Debug "    dump device is not a logical volume."
+          rlogvol=$( echo $logvol | sed -e "s:^/dev/dsk/:/dev/rdsk/:" )
+          DumpSize=$( diskinfo $rlogvol | grep "size" | awk '{print $2}')
+          DumpSize=$( expr $DumpSize / 1024 )
+        else
+          DumpSize=$(lvdisplay $logvol | grep "LV Size" | awk '{print $4}')
+          Debug "    $logvol DumpSize = ${DumpSize}"
+        fi
+        Debug "  Adding $DumpSize to DumpDiskCapacity = ${DumpDiskCapacity}"
+        ((DumpDiskCapacity = DumpDiskCapacity + DumpSize))
+      done
+      Debug "  DumpDiskCapacity =${DumpDiskCapacity}"
+    fi
+    Debug "Checking how much to dump"
+    # add kernel size to memory plus 2Mb safety factor
+    ((TotalMemoryDumpSize = MemoryDumpSize + KernelSizeMb + KernelSafetyFactor))
+    Debug "  REAL_MEM = $REAL_MEM"
+    Debug "  memory =               $memory"
+    Debug "  KernelSizeMb =           $KernelSizeMb"
+    Debug "  KernelSafetyFactor =     $KernelSafetyFactor"
+    Debug "                     ---------"
+    Debug "  TotalMemoryDumpSize =  $TotalMemoryDumpSize"
+
+    if (($BATCH))
+    then
+      print -n "."
+    fi
+# now check to see if everything is okay.
+#
+# Updated: 12/15/98 by Greg Sterling
+# I updated this code to consider the CRASHCONF utility on V11.x. The
+# TotalMemoryDumpSize parameter is only relevent for a FULL crash dump.
+# Normal system crashes (if there is such a thing), can be captured within
+# smaller dump spaces due to the CRASHUTIL utility.
+#
+    case $osmajor in
+      11 )
+         if (( DumpDiskCapacity < TotalMemoryDumpSize ))
+         then
+           if (( DumpDiskCapacity < crashconf_dumpsize ))
+           then
+             print "WARNING (${sysname}): Dump space is underconfigured!" \
+                     >> /tmp/sysinfo.$$.errwarn
+           else
+             print "WARNING (${sysname}): Dump space is underconfigured \
+for a FULL crashdump." >> /tmp/sysinfo.$$.errwarn
+             print "WARNING (${sysname}): Dump space is adequate for the \
+CRASHCONF configuration." >> /tmp/sysinfo.$$.errwarn
+           fi
+         fi
+       ;;
+
+ 8 | 9 | 10 )
+         if (( DumpDiskCapacity < TotalMemoryDumpSize ))
+         then
+           print "WARNING (${sysname}): Dump space is under configured!" \
+                   >> /tmp/sysinfo.$$.errwarn
+         fi
+       ;;
+  esac
+  if ((! (($exit_code)) ))
+  then
+    exit_code=${WARN}
+  fi
+
+  if (( ! $SAVECORE ))
+  then
+    SAVECORE=Disabled
+    if ((! (($exit_code)) ))
+    then
+      exit_code=${WARN}
+    fi
+
+    case $osmajor in
+      8 | 9 | 10 )
+       print "WARNING (${sysname}): Savecore is not enabled!" \
+                 >> /tmp/sysinfo.$$.errwarn
+      ;;
+
+      11 )
+       if [[ -z $CRASHCONF_ENABLED ]]
+       then
+        Debug "Savecore is Disabled and CrashConf is Disabled."
+        print "WARNING (${sysname}):Both Savecore and crashconf are disabled!" \
+                 >> /tmp/sysinfo.$$.errwarn
+       fi
+      ;;
+    esac
+
+  else
+
+
+  # Now check if enough disk space exists to hold savecore
+  Debug "Checking for sufficient savecore space"
+  COMPRESSION="Not Applicable"
+  if [[ -d $SAVECORE_DIR ]] 
+  then
+    #SaveCoreAvail=$(bdf $SAVECORE_DIR | grep /dev/ | awk '{print $4}')
+    # fix for non-standard lvol names
+    SaveCoreAvail=$(df -b $SAVECORE_DIR | awk '{print $5}')
+    ((SaveCoreAvail = SaveCoreAvail / 1024))
+    Debug "  SAVECORE=$SAVECORE"
+    Debug "  SAVECORE_DIR=$SAVECORE_DIR" 
+    Debug "  SaveCoreAvail=$SaveCoreAvail" 
+  else
+    SaveCoreAvail=0
+    if ((! (($exit_code)) ))
+    then
+      exit_code=${WARN}
+    fi
+    print "WARNING (${sysname}): Directory $SAVECORE_DIR does not exist!" >> /tmp/sysinfo.$$.errwarn
+  fi
+    if ((($osmajor == 10))  && (($osminor >= 10))) || (( $osmajor > 10 ))
+    then
+      COMPRESSION="Available"
+      compression_factor=${compression_factor:-50}
+      ((MemoryCompression = (TotalMemoryDumpSize * $compression_factor) / 100 ))
+      ((CompressedMemoryDumpSize = TotalMemoryDumpSize - MemoryCompression))
+
+      Debug "  Checking for savecore compression"
+      Debug "    Compress_option=$Compress_Option"
+      Debug "    Compression=$COMPRESSION"
+      Debug "    compression_factor=$compression_factor"
+      Debug "    CompressedMemoryDumpSize after = $CompressedMemoryDumpSize"
+      if (( $CompressedMemoryDumpSize  > $SaveCoreAvail ))
+      then
+        if ((! (($exit_code)) ))
+        then
+          exit_code=${WARN}
+        fi
+        print "WARNING (${sysname}): Insufficient file space to hold savecore!" >> /tmp/sysinfo.$$.errwarn
+      fi
+    else
+      if (( $TotalMemoryDumpSize  > $SaveCoreAvail ))
+      then
+        if ((! (($exit_code)) ))
+        then
+          exit_code=${WARN}
+        fi
+        print "WARNING (${sysname}): Insufficient file space to hold savecore!" >> /tmp/sysinfo.$$.errwarn
+      fi
+    fi
+    SaveCoreAvail_Out=$SaveCoreAvail
+    SAVECORE=Enabled
+  fi
+
+  TotalMemoryDumpSizeOut=$TotalMemoryDumpSize
+
+# now let's print it.
+  if (($HTML))
+  then
+    print "
" >> /tmp/sysinfo.$$.sysout + print "" >> /tmp/sysinfo.$$.sysout + print "

" >> /tmp/sysinfo.$$.sysout + print "" >> /tmp/sysinfo.$$.sysout + print "System Data" >> /tmp/sysinfo.$$.sysout + print "

" >> /tmp/sysinfo.$$.sysout + print "
" >> /tmp/sysinfo.$$.sysout
+  else
+    print "" >> /tmp/sysinfo.$$.sysout
+    print "SYSTEM DATA" >> /tmp/sysinfo.$$.sysout
+    print "===========" >> /tmp/sysinfo.$$.sysout
+  fi
+  print "HOSTNAME:      ${sysname}" >> /tmp/sysinfo.$$.sysout
+  print "SYSTEM:        ${system}" >> /tmp/sysinfo.$$.sysout
+  print "MODEL:         ${series}" >> /tmp/sysinfo.$$.sysout
+  if [[ -n ${pa_chip} ]]
+  then
+    print "RISC CHIP:     ${pa_chip}" >> /tmp/sysinfo.$$.sysout
+    print "PA VERSION:    ${pa_ver}" >> /tmp/sysinfo.$$.sysout
+  fi
+  if [[ -n ${serial_number} ]]
+  then
+    print "SERIAL #:      ${serial_number}" >> /tmp/sysinfo.$$.sysout
+  fi
+  print "SYSTEM ID:     ${machine_id}" >> /tmp/sysinfo.$$.sysout
+  print "CPU SPEED:     ${cpu_speed} MHz" >> /tmp/sysinfo.$$.sysout
+  print "CPUS:          ${processor_count} active processor(s) " >> /tmp/sysinfo.$$.sysout
+  print "MEMORY:        ${memory} Mbytes of memory." >> /tmp/sysinfo.$$.sysout
+  print -n "HP-UX VERSION: ${osletter}.${osmajor}.${osminor}" >> /tmp/sysinfo.$$.sysout
+  if (( $osmajor == 11 ))
+  then
+    print "   ${k32_64} Bit" >> /tmp/sysinfo.$$.sysout
+  else
+    print "" >> /tmp/sysinfo.$$.sysout
+  fi
+  print "USER LICENSE:  ${license}" >> /tmp/sysinfo.$$.sysout
+  print "LAST BOOT:     ${boot_time}" >> /tmp/sysinfo.$$.sysout
+  print "LAST PATCH:    ${last_patch}\n" >> /tmp/sysinfo.$$.sysout
+  print "" >> /tmp/sysinfo.$$.sysout
+
+  if (( ${cstm_ok} == 0 ))
+  then
+    print "System H/W data" >> /tmp/sysinfo.$$.sysout
+    print "===============" >> /tmp/sysinfo.$$.sysout
+    print "  Processor data" >> /tmp/sysinfo.$$.sysout
+    print -- "  --------------" >> /tmp/sysinfo.$$.sysout
+    print "       H/W          PDC        Cache Size    TLB Size" >>/tmp/sysinfo.$$.sysout
+    print "   path  slot    rev level    Inst   Data  Inst   Data" >>/tmp/sysinfo.$$.sysout
+    print -- "  -----------------------------------------------------" >>/tmp/sysinfo.$$.sysout
+    cat $cstm_cpu_out2 | awk -F: '
+        {printf("%8s%6s%14s%7s%7s%6s%6s\n",$1,$2,$3,$4,$5,$6,$7)}
+        ' >> /tmp/sysinfo.$$.sysout
+
+    print "" >> /tmp/sysinfo.$$.sysout
+    print    "  Memory Information" >> /tmp/sysinfo.$$.sysout
+    print -- "  ------------------" >> /tmp/sysinfo.$$.sysout
+    #print "    Total Physical Memory    = $cstm_tot_phys MB" \
+    #         >> /tmp/sysinfo.$$.sysout
+    #print "    Total Configured Memory  = $cstm_tot_conf MB" \
+    #         >> /tmp/sysinfo.$$.sysout
+    #print "    Memory Page Size         = $cstm_page_size Bytes" \
+    #         >> /tmp/sysinfo.$$.sysout
+    #print "$cstm_interleave" >> /tmp/sysinfo.$$.sysout
+    #print "" >> /tmp/sysinfo.$$.sysout
+
+    cat ${cstm_mem_out} | tail -n +10 | sed -e '/^$/d' >> /tmp/sysinfo.$$.sysout
+    print "" >> /tmp/sysinfo.$$.sysout
+  fi
+
+  print "SWAP DATA" >> /tmp/sysinfo.$$.sysout
+  print "=========" >> /tmp/sysinfo.$$.sysout
+  if (( $osmajor >= 10 ))
+  then
+    Debug "`swapinfo -dtfnrMa`"
+  else
+    Debug "`swapinfo -dtfa`"
+  fi
+
+  swapinfo -m | grep dev | \
+      awk '{printf "%6s MB on %s\n",$2,$9}' >> /tmp/sysinfo.$$.sysout
+  swapinfo -m | grep fs | \
+      awk '{printf "%6s MB on %s\n",$2,$9}' >> /tmp/sysinfo.$$.sysout
+  swapinfo -m | grep memory | \
+      awk '{printf "%6s MB on memory\n",$2}' >> /tmp/sysinfo.$$.sysout
+  print -- "  ----" >> /tmp/sysinfo.$$.sysout
+
+  if (( $osmajor >= 10 ))
+  then
+    swapinfo -mdfMt | grep total | \
+        awk '{printf "%6s MB Total \n",$2}' >> /tmp/sysinfo.$$.sysout
+  else
+    swapinfo -mdft | grep tot | \
+        awk '{printf "%6s MB Total \n",$2}' >> /tmp/sysinfo.$$.sysout
+  fi
+  print "" >> /tmp/sysinfo.$$.sysout
+  print "DUMP DISKS"            >> /tmp/sysinfo.$$.sysout
+  print "=========="            >> /tmp/sysinfo.$$.sysout
+  lvlnboot -v /dev/vg00 2>&1 | grep Dump | awk '{print "  "$0}' >> \
+               /tmp/sysinfo.$$.sysout 
+  print "" >> /tmp/sysinfo.$$.sysout
+  print "DUMP DATA" >> /tmp/sysinfo.$$.sysout
+  print "=========" >> /tmp/sysinfo.$$.sysout
+  print "SAVECORE:                  ${SAVECORE}" >> /tmp/sysinfo.$$.sysout
+  case ${SAVECORE} in
+    D* )
+      ;;
+    E* )
+      print "SAVECORE DIR:              ${SAVECORE_DIR}" >> \
+                   /tmp/sysinfo.$$.sysout
+      if ((($osmajor == 10)) && (($osminor >= 10))) || (( $osmajor > 10 ))
+      then
+        print "SAVECORE COMPRESSION:      ${COMPRESSION}" >> \
+                   /tmp/sysinfo.$$.sysout
+        print "COMPRESSION OPTION:        ${Compress_Option}" >> \
+                   /tmp/sysinfo.$$.sysout
+        print "COMPRESSION FACTOR:          ${compression_factor} %" >> \
+                   /tmp/sysinfo.$$.sysout
+        print "COMPRESSED MEMORY TO DUMP:   ${CompressedMemoryDumpSize} MB" >> \
+                   /tmp/sysinfo.$$.sysout
+        print "SAVECORE CAPACITY:           ${SaveCoreAvail_Out} MB\n" >> \
+                   /tmp/sysinfo.$$.sysout
+      fi
+      ;;
+  esac
+  
+#
+# Added: Greg Sterling
+# Added code to output the status of the CrashConf configuration.
+#
+  if (($osmajor > 10))
+  then
+    if [[ ! -z $CRASHCONF_ENABLED ]]
+    then
+      print "" >> /tmp/sysinfo.$$.sysout
+      if [[ -z $CRASHCONF_READ_FSTAB ]]
+      then
+        print "CRASHCONF             : Enabled" >> /tmp/sysinfo.$$.sysout
+      else
+        print "CRASHCONF             : Enabled (referencing /etc/fstab)" >> \
+                /tmp/sysinfo.$$.sysout
+      fi
+      if [[ -z $CRASHCONF_REPLACE ]]
+      then
+        print "   Replace Option     : CrashConf values have been Added to existing Kernel Definitions." >> /tmp/sysinfo.$$.sysout
+      else
+        print "   Replace Option     : CrashConf values REPLACE existing Kernel Definitions." >> /tmp/sysinfo.$$.sysout
+      fi
+
+      print "   Included Pages     : $CRASH_INCLUDED_PAGES" >> \
+                /tmp/sysinfo.$$.sysout
+      print "   Excluded Pages     : $CRASH_EXCLUDED_PAGES" >> \
+                /tmp/sysinfo.$$.sysout
+      print "   FSTAB File Entries : $crashconf_fstabs" >> \
+                /tmp/sysinfo.$$.sysout
+      print "   CRASHCONF Configured MEMORY TO DUMP: ${crashconf_dumpsize} MB" \
+                 >> /tmp/sysinfo.$$.sysout
+    else
+      print "CRASHCONF             : Disabled" >> /tmp/sysinfo.$$.sysout
+    fi
+    print "" >> /tmp/sysinfo.$$.sysout
+  fi
+
+  print "KERNEL SIZE:                ${kernel_size} Bytes." >> \
+                     /tmp/sysinfo.$$.sysout
+  print "TOTAL MEMORY TO DUMP:        ${TotalMemoryDumpSizeOut} MB" >> \
+                     /tmp/sysinfo.$$.sysout
+
+  print "DUMP DISK CAPACITY:          ${DumpDiskCapacity} MB" >> \
+                   /tmp/sysinfo.$$.sysout
+
+  f_display_file /tmp/sysinfo.$$.sysout 
+  f_display_file /tmp/sysinfo.$$.stmout 
+  #f_display_file /tmp/sysinfo.$$.dumpwarn 
+  print "" >> /tmp/sysinfo.$$.sysout
+
+#  if (($BATCH))
+#  then
+    print 
+#  fi
+}                # end of f_get_system_data
+
+function f_call_getkinfo
+{
+/usr/sam/lbin/getkinfo -b -o ${sam_kinfo}
+}
+
+function get_all_parms
+{
+Debug "starting get_all_parms"
+awk '
+     BEGIN { FS = "\n"; RS = "}" }
+     {print $0}
+    ' $sam_kinfo | \
+    awk '
+         /KC_PARAM_NAME/      {name = $3;  printf("%s,", name)}
+         /KC_PARAM_CLASS/     {class = $3 " " $4; printf("%s\n", class)}
+        ' #| sed -e 's/\"*//g'
+}
+
+function get_all_classes
+{
+Debug "starting get_all_classes"
+awk ' BEGIN { FS = "\n"; RS = "}" }
+      $0 ~ /'\"$parm\"'/ {print $0}' $sam_kinfo \
+        | awk '
+          /^KC_PARAM_CLASS/     {printf("%s %s\n",$3,$4)}
+          ' #| sed -e 's/\"*//g'
+}
+
+function f_query_sam
+{
+  sam_parm=$1
+  type=$2
+    awk ' BEGIN { FS = "\n"; RS = "}" }
+      $0 ~ /'$sam_parm'/ {print $0}' $sam_kinfo \
+          | awk '
+          /^KC_PARAM_NAME/      {name = $3}
+          /^KC_PARAM_STATUS/    {status = $3}
+          /^KC_PARAM_DEFAULT/   {default = $3}
+          /^KC_PARAM_MAX/       {max = $3}
+          /^KC_PARAM_MIN/       {min = $3}
+          /^KC_PARAM_CLASS/     {class = $3" " $4}
+          /^KC_PARAM_DESC/      {desc = ""
+                                 for(i=3;i<=NF;i++)
+                                 desc = desc $i " ";
+                                 }
+
+          END { printf("%s@%s@%s@%s@%s@%s@%s\n",
+                        name,status,default,max,min,desc,class)}
+    ' | sed -e 's/\"*//g'
+
+}
+
+#===================================================================
+# f_get_kernel_data
+#        This function queries the kernel for various parameters.
+#===================================================================
+function f_get_kernel_data 
+{
+
+  Debug "Beginning KERNEL check."
+  if (($BATCH))
+  then
+    print -n "collecting kernel data "
+    print "" >> /tmp/sysinfo.$$.kernout
+  else
+    print    "Collecting kernel metrics....please wait."
+  fi
+ 
+  if (($HTML))
+  then
+    print "
" >> /tmp/sysinfo.$$.kernout + print "" >> /tmp/sysinfo.$$.kernout + print "

" >> /tmp/sysinfo.$$.kernout + print "

TOP

" >> /tmp/sysinfo.$$.kernout + print "" >> /tmp/sysinfo.$$.kernout + if (( ${FULL_KERNEL} )) + then + print "Kernel Parms (Verbose Listing)" >> /tmp/sysinfo.$$.kernout + else + print "Kernel Parms (Brief Listing)" >> /tmp/sysinfo.$$.kernout + fi + print "

" >> /tmp/sysinfo.$$.kernout + print "
" >> /tmp/sysinfo.$$.kernout
+  else
+    print "KERNEL PARAMETERS" >> /tmp/sysinfo.$$.kernout
+    print "=================" >> /tmp/sysinfo.$$.kernout
+  fi
+
+Debug "calling f_call_getkinfo -> ${sam_kinfo}"
+f_call_getkinfo
+
+Debug "calling get_all_parms -> ${all_parms_file}"
+get_all_parms > ${all_parms_file}
+Debug " parm names = `cat ${all_parms_file}`"
+
+Debug "calling get_all_classes -> ${all_classes_file}"
+get_all_classes | sort -t , -k 7 | uniq  > ${all_classes_file}
+    #sed -e 's/ /_/g' -e 's/_$//g'  > ${all_classes_file}
+Debug " class names = `cat ${all_classes_file}`"
+
+exec 3< ${all_classes_file}
+exec 4< ${all_parms_file}
+while read -u3 class
+do
+  if (($BATCH))
+  then
+    print -n "."
+  fi
+  print_class=$(echo $class | sed -e 's/\"*//g')
+  if (($HTML))
+  then
+    print "

${print_class} Metrics

" >> /tmp/sysinfo.$$.kernout + else + print "${print_class} Metrics" >> /tmp/sysinfo.$$.kernout + print "============================" >> /tmp/sysinfo.$$.kernout + fi + Debug "Class = ${class}" + for parm1 in $(grep "${class}" ${all_parms_file} | awk -F, '{print $1}') + do + Debug "Parm1 = $parm1" + if (($FULL_KERNEL)) + then + f_query_sam ${parm1} | \ + awk 'BEGIN {FS = "@"} + {printf("%s\n",$1) + printf(" Title: %s\n",$6) + printf(" Current: %s\n",$2) + printf(" Default: %s\n",$3) + printf(" Min: %s\n",$5) + printf(" Max: %s\n",$4) + #printf(" Class: %s\n",$7) + } '>> /tmp/sysinfo.$$.kernout + else + f_query_sam ${parm1} | \ + awk 'BEGIN {FS = "@"} + {printf(" %-20s %s\n",$1,$2) + } ' >> /tmp/sysinfo.$$.kernout + fi + done +print "" >> /tmp/sysinfo.$$.kernout +done + print "" >> /tmp/sysinfo.$$.kernout + f_display_file /tmp/sysinfo.$$.kernout + +if (($PASS)) +then + if (($DB_on_this_sys)) + then + dbc_max=$(grep dbc_max /tmp/sysinfo.$$.kernout | awk '{print $2}') + if (( dbc_max > 10 )) + then + print "WARNING ($sysname): dbc_max is set to ${dbc_max} on DB server" >> \ + /tmp/sysinfo.$$.sapwarn + fi + fi +fi + f_display_file /tmp/sysinfo.$$.kernwarn + if (($BATCH)) + then + print "" + fi +} # end of f_get_kernel_data + +#=================================================================== +# f_get_9x_kernel_data +# This function queries the kernel for various parameters. +#=================================================================== +function f_get_9x_kernel_data +{ + + Debug "Beginning 9x KERNEL check." + if (($BATCH)) + then + print -n "collecting kernel data " + fi + shmmni=$(echo 'shmmni/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + maxfiles=$(echo 'maxfiles/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi + shmmax=$(echo 'shmmax/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + shmseg=$(echo 'shmseg/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + shmem=$(echo 'shmem/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi + maxfiles_lim=$(echo 'maxfiles_lim/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + maxuprc=$(echo 'maxuprc/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi + nproc=$(echo 'nproc/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + nfile=$(echo 'nfile/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi + nflocks=$(echo 'nflocks/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + ninode=$(echo 'ninode/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi +# PA8000 chips support variable page sizes + super_page_supp=$(echo cpu_has_var_size_pages/D | adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + case $super_page_supp in + 0) super_page_support=no;; + 1) super_page_support=yes;; + *) super_page_support=n/a;; # not found in kernel + esac + if (($BATCH)) + then + print -n "." + fi + npty=$(echo 'npty/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') +# check for asynch disc writes enabled + fs_async=$(echo 'fs_async/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if ((${fs_async} == 0)) + then + fs_async=no + else + fs_async=yes + fi + if (($BATCH)) + then + print -n "." + fi + nbuf=$(echo 'nbuf/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + bufpages=$(echo 'bufpages/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + + if (($BATCH)) + then + print -n "." + fi + msgmap=$(echo 'msgmap/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi + maxdsiz=$(echo 'maxdsiz/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + maxssiz=$(echo 'maxssiz/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi + maxtsiz=$(echo 'maxtsiz/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + maxuprc=$(echo 'maxuprc/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi + ncdnode=$(echo 'ncdnode/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi + sema=$(echo 'sema/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi + semmap=$(echo 'semmap/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi +# get LVM related kernel parameters + maxvgs=$(echo 'maxvgs/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + lv_vgs_opn=$(echo 'lv_vgs_opn/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi + lv_lvs_opn=$(echo 'lv_lvs_opn/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + lv_pbuf_cnt=$(echo 'lv_pbuf_cnt/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi + lv_pbuf_inuse=$(echo 'lv_pbuf_inuse/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + lv_pbuf_maxuse=$(echo 'lv_pbuf_maxuse/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + if (($BATCH)) + then + print -n "." + fi + lv_pbuf_pending_Q=$(echo 'lv_pbuf_pending_Q/D'|adb $kernel /dev/kmem \ + | tail -1 | awk '{print $2}') + + if (($HTML)) + then + print "
" >> /tmp/sysinfo.$$.kernout + print "" >> /tmp/sysinfo.$$.kernout + print "" >> /tmp/sysinfo.$$.kernout + print "

Kernel Data

" >> /tmp/sysinfo.$$.kernout + print "
" >> /tmp/sysinfo.$$.kernout
+  else
+    print "KERNEL DATA" >> /tmp/sysinfo.$$.kernout
+    print "===========" >> /tmp/sysinfo.$$.kernout
+  fi
+  print "Super page support (cpu_has_var_size_pages) $super_page_support" >> /tmp/sysinfo.$$.kernout
+  print "" >> /tmp/sysinfo.$$.kernout
+  print "Max shared memory segments in system (shmmni)$shmmni" >> /tmp/sysinfo.$$.kernout
+  print "Enable Sys V Shared Memory(shmem)            ${shmem}" >> /tmp/sysinfo.$$.kernout
+
+  print "" >> /tmp/sysinfo.$$.kernout
+  print "Soft file limit per process (maxfiles)       $maxfiles" >> /tmp/sysinfo.$$.kernout
+  print "Hard file limit per process (maxfiles_lim)   $maxfiles_lim" >> /tmp/sysinfo.$$.kernout
+  print "Max number of processes (nproc)              $nproc" >> /tmp/sysinfo.$$.kernout
+  print "Max number of user processes (maxuprc)       $maxuprc" >> /tmp/sysinfo.$$.kernout
+  print "Number of open files in system (nfile)       $nfile" >> /tmp/sysinfo.$$.kernout
+  print "Maximum number of file locks (nflocks)       $nflocks" >> /tmp/sysinfo.$$.kernout
+  print "Max number of in-core inodes (ninode)        $ninode" >> /tmp/sysinfo.$$.kernout
+  print "Asynchronous disk writes allowed (fs_async)  $fs_async" >> /tmp/sysinfo.$$.kernout
+  print "Number of pseudo-teletypes (npty)            $npty" >> /tmp/sysinfo.$$.kernout
+  print "Number of filesystem buffer headers (nbuf)   $nbuf" >> /tmp/sysinfo.$$.kernout
+  print "Number of buffer pages in cache (bufpages)   $bufpages" >> /tmp/sysinfo.$$.kernout
+  print "Max Number of Message Map Entries (msgmap)   $msgmap" >> /tmp/sysinfo.$$.kernout
+  print "Max Data Segment Size (Bytes) (maxdsiz)      $maxdsiz" >> /tmp/sysinfo.$$.kernout
+  print "Max Stack Segment Size (Bytes) (maxssiz)     $maxssiz" >> /tmp/sysinfo.$$.kernout
+  print "Max Text Segment Size (Bytes) (maxtsiz)      $maxtsiz" >> /tmp/sysinfo.$$.kernout
+  print "Enable Sys V Semaphores (sema)               $sema" >> /tmp/sysinfo.$$.kernout
+
+  if (($lvm_installed))
+  then
+    print "" >> /tmp/sysinfo.$$.kernout
+    print "LVM Parms" >> /tmp/sysinfo.$$.kernout
+    print "  Max number of volume groups (maxvgs)       $maxvgs" >> /tmp/sysinfo.$$.kernout
+    print "  Number of open volume groups (lv_vgs_opn)  $lv_vgs_opn" >> /tmp/sysinfo.$$.kernout
+    print "  Number of open logical volumes (lv_lvs_opn)$lv_lvs_opn" >> /tmp/sysinfo.$$.kernout
+    print "  Lvol pbuf count (lv_pbuf_cnt)              $lv_pbuf_cnt" >> /tmp/sysinfo.$$.kernout
+    print "  Lvol pbuf current usage (lv_pbuf_inuse)    $lv_pbuf_inuse" >> /tmp/sysinfo.$$.kernout
+    print "  Lvol pbuf high water mark (lv_pbuf_maxuse) $lv_pbuf_maxuse" >> /tmp/sysinfo.$$.kernout
+    print "  lv_pbuf_pending_Q:                         $lv_pbuf_pending_Q \n" >> /tmp/sysinfo.$$.kernout
+  fi
+  f_display_file /tmp/sysinfo.$$.kernout
+  f_display_file /tmp/sysinfo.$$.kernwarn
+  if (($BATCH))
+  then
+    print ""
+  fi
+}                # end of f_get_9x_kernel_data
+
+
+#===================================================================
+# f_get_network_data
+#        This function queries each lan card and retrieves information
+#        for each one.
+#===================================================================
+function f_get_network_data
+{
+  Debug "Beginning NETWORK check."
+  if (($BATCH))
+  then
+    print -n "scanning network cards "
+  fi
+  GetCurrentDNS
+  #DOMAIN=$(nslookup ${sysname} | grep Name | grep -v Server | cut -d. -f2-6)
+  default_router=$( netstat -r | grep default | awk '{print $2}')
+  default_router_ip=$( netstat -rn | grep default | awk '{print $2}')
+  if (($HTML))
+  then
+    print "
" >> /tmp/sysinfo.$$.netout + print "" >> /tmp/sysinfo.$$.netout + print "

" >> /tmp/sysinfo.$$.netout + print "

TOP

" >> /tmp/sysinfo.$$.netout + print "" >> /tmp/sysinfo.$$.netout + print "Network Data

" >> /tmp/sysinfo.$$.netout + print "
" >> /tmp/sysinfo.$$.netout
+  else
+    print "NETWORK DATA" >> /tmp/sysinfo.$$.netout
+    print "============" >> /tmp/sysinfo.$$.netout
+  fi
+  print "DOMAIN NAME:    ${CURRENT_DOMAIN}" >> /tmp/sysinfo.$$.netout
+  print -n "DNS SERVER:     ${CURRENT_DNS_SERVER}  " >> /tmp/sysinfo.$$.netout
+  print "(${CURRENT_DNS_SERVER_IP})" >> /tmp/sysinfo.$$.netout
+  print -n "DEFAULT ROUTER: ${default_router}  "  >> /tmp/sysinfo.$$.netout
+  print "(${default_router_ip})"  >> /tmp/sysinfo.$$.netout
+  print "" >> /tmp/sysinfo.$$.netout
+
+  #for CARD in $(netstat -in | grep lan | awk '{print $1}' | sed 's/\*//')
+  # 1.42 change to handle multiple I/F cards
+  if (( ${osmajor} >= 10 ))
+  then
+    card_list=$(/etc/lanscan -i | awk '{print $1}')
+    Debug " 10.x using lanscan -i"
+  else
+    card_list=$(/etc/lanscan | tail +3 | awk '{printf("%s\n",$5)}')
+    Debug " 9.x using lanscan "
+  fi
+  for CARD in ${card_list}
+  do
+    ifconfig "${CARD}" > /tmp/sysinfo.$$.ipdata 2>&1
+    if [ $? -eq 0 ]
+    then
+      Debug " checking lan card -> $CARD"
+      #lanconfig "$CARD" >> /tmp/sysinfo.$$.ipdata 2>&1
+      #card_type=$(lanconfig "$CARD" | grep ${CARD} | awk '{print $2}')
+      if (( ${osmajor} >= 11 ))
+      then
+        card_type=$(lanscan -im  | grep "${CARD} " | awk '{print $NF}')
+      else
+        card_type=$(lanscan | grep "${CARD} " | awk '{print $8}')
+      fi
+      Debug "  card_type=$card_type"
+      #MWR fix for incorrect handling of multiple lan cards
+      #macaddr=$(/etc/lanscan | grep x | awk '{print $2}')
+      macaddr=$(/etc/lanscan | grep "${CARD} " | awk '{print $2}')
+      lan_hw_addr=$(/etc/lanscan | grep "${CARD} " | awk '{print $1}')
+      nmid=$(/etc/lanscan | grep "${CARD} " | awk '{print $7}')
+      ip_addr=$(grep inet /tmp/sysinfo.$$.ipdata | awk '{print $2}')
+      ip_addr_name=$(LookupName ${ip_addr})
+      lanspeed=$(lanadmin -s ${nmid} | awk '{print $3}')
+      netmask=$(grep inet /tmp/sysinfo.$$.ipdata | awk '{print $4}')
+
+      #convert hex netmask to decimal-dot.
+      typeset -Z8  hex=0${netmask#0x}; typeset +Z hex
+      typeset -L2  ott=
+      typeset -i10 dec=
+      netmask=""
+      while [ "$hex" ]; do
+        ott=$hex
+        dec=16#$ott
+        netmask=$netmask.$dec
+        hex=${hex#??}
+      done
+      netmask=${netmask#.}
+
+      broadcast=$(grep inet /tmp/sysinfo.$$.ipdata | awk '{print $6}')
+      if (($HTML))
+      then
+        print "
" >> /tmp/sysinfo.$$.netout + print "

INTERFACE DATA for ${CARD}

" >> /tmp/sysinfo.$$.netout + print "
" >> /tmp/sysinfo.$$.netout
+      else
+        print "INTERFACE DATA for ${CARD}" >> /tmp/sysinfo.$$.netout
+        print "===========================" >> /tmp/sysinfo.$$.netout
+      fi
+      #print "NMID:        ${nmid}" >> /tmp/sysinfo.$$.netout
+      print "H/W ADDR:    ${lan_hw_addr}" >> /tmp/sysinfo.$$.netout
+      print "CARD TYPE:   ${card_type}" >> /tmp/sysinfo.$$.netout
+      print "SPEED:       ${lanspeed}" >> /tmp/sysinfo.$$.netout
+      print "MAC ADDRESS: ${macaddr}" >> /tmp/sysinfo.$$.netout
+      print -n "IP ADDRESS:  ${ip_addr}" >> /tmp/sysinfo.$$.netout
+      print "   (${ip_addr_name})" >> /tmp/sysinfo.$$.netout
+      print "BROADCAST:   ${broadcast}" >> /tmp/sysinfo.$$.netout
+      print "NETMASK:     ${netmask}\n" >> /tmp/sysinfo.$$.netout
+    fi
+    if (($BATCH))
+    then
+      print -n "."
+    fi
+  done
+  f_display_file /tmp/sysinfo.$$.netout 
+  if (($BATCH))
+  then
+    print
+  fi
+}                # end of f_get_network_data
+
+function query_EMC_disks
+{
+Debug "Beginning query_EMC_disks"
+if [ -f ${where}/inq.hp ]
+then
+  Debug "  found inq.hp"
+  #inq.hp  
+else
+  Debug "  Could not find inq.hp....no query done."
+fi
+
+}
+rawDiskFilter () {
+    # Filters out the first raw disk file after an HP Claimed disk
+        awk '
+        ( $0 ~ "CLAIMED" )              { hit=0 }
+        ( $0 ~ "CLAIMED" && $0 ~ "HP")  { hit=1; next }
+        ( $0 !~ "CLAIMED" && hit==1)    { hit=0; print $2 }
+        '
+} # rawDiskFilter()
+
+
+#===================================================================
+# f_get_physical_disk_data
+#        This function scans each physical disk.
+#        It uses ioscan to collect disk info and the queries each
+#        device using diskinfo. 
+#        The output is displayed in sorted order.
+#===================================================================
+function f_get_physical_disk_data
+{
+  Debug "Beginning PHYSICAL DISK check."
+# first let's print header information
+  if (($HTML))
+  then
+    print "
" >> /tmp/sysinfo.$$.physinfoh + print "" >> /tmp/sysinfo.$$.physinfoh + print "

" >> /tmp/sysinfo.$$.physinfoh + print "

TOP

" >> /tmp/sysinfo.$$.physinfoh + print "" >> /tmp/sysinfo.$$.physinfoh + print "Physical Disk Data

" >> /tmp/sysinfo.$$.physinfoh + print "
"                       >> /tmp/sysinfo.$$.physinfoh
+    print "
" >> /tmp/sysinfo.$$.boot + print "

Bootable Disks

" >> /tmp/sysinfo.$$.boot + print "
"                >> /tmp/sysinfo.$$.boot
+    print    "Volume Name                 H/W Path    Auto Boot String" >> \
+              /tmp/sysinfo.$$.boot
+    print -- "--------------            ------------  ----------------" >> \
+              /tmp/sysinfo.$$.boot
+    print ""                     >> /tmp/sysinfo.$$.boot
+  else
+    print    "PHYSICAL DISK DATA" >> /tmp/sysinfo.$$.physinfoh
+    print    "==================" >> /tmp/sysinfo.$$.physinfoh
+    print    "BOOTABLE DISKS" >> /tmp/sysinfo.$$.boot
+    print    "==============" >> /tmp/sysinfo.$$.boot
+    print    "Volume Name                    H/W Path    Auto Boot String" >> \
+              /tmp/sysinfo.$$.boot
+    print -- "--------------               ------------  ----------------" >> \
+              /tmp/sysinfo.$$.boot
+  fi
+
+ # Header for XP256 Information
+  print > /tmp/sysinfo.$$.256header
+  print "XP256 Disc Array Information" >> /tmp/sysinfo.$$.256header
+  print "============================" >> /tmp/sysinfo.$$.256header
+  print -n "                                      " >> /tmp/sysinfo.$$.256header
+  print "                                    Size" >> /tmp/sysinfo.$$.256header
+  print -n "Device File            Port  Scsi  Lun CU:Ldev Serial#" \
+                                   >> /tmp/sysinfo.$$.256header
+  print "           Type     (MB)" >> /tmp/sysinfo.$$.256header
+
+ # Header for NIKE Array Information
+  print > /tmp/sysinfo.$$.arrayheader
+  print "Disk Array Information" >> /tmp/sysinfo.$$.arrayheader
+  print "======================" >> /tmp/sysinfo.$$.arrayheader
+  
+  if (($PMAP))
+  then
+    if (($HTML))
+    then
+      print "
" >> /tmp/sysinfo.$$.pmap + print "" >> /tmp/sysinfo.$$.pmap + print "

" >> /tmp/sysinfo.$$.pmap + print "

TOP

" >> /tmp/sysinfo.$$.pmap + print "" >> /tmp/sysinfo.$$.pmap + print "Physical Disk To Logical Volume Mapping

" >> /tmp/sysinfo.$$.pmap + print "
"        >> /tmp/sysinfo.$$.pmap
+    else
+      print    "PHYSICAL DISK TO LOGICAL VOLUME MAPPING" >> /tmp/sysinfo.$$.pmap
+      print    "=======================================" >> /tmp/sysinfo.$$.pmap
+    fi
+    print -n "Physical Disk                           " >> /tmp/sysinfo.$$.pmap
+    print    "     Alternate Link" >> /tmp/sysinfo.$$.pmap
+    print    "       Logical Volume     LE        PE" >> /tmp/sysinfo.$$.pmap
+    print -n -- "----------------------------------------" >> /tmp/sysinfo.$$.pmap
+    print --    "-----------------------" >> /tmp/sysinfo.$$.pmap
+  fi
+  Debug "  calling check_if_scan_done"
+  check_if_scan_done io_scan
+  print -n "scanning physical disks "
+
+#get physical disk names
+  if (( $osmajor >= 10 ))
+  then
+    Debug "Found 10.x system"
+#   hw_path=$(ioscan -kfC disk | sed '1,/^=/d' | awk '{print $3}')
+    hw_path=$(grep disk $io_scan_out | awk -F: '{print $11}' | sort)
+    Debug "hw_path=$hw_path"
+    for hwpath in ${hw_path}
+    do
+      Debug "hwpath=${hwpath}"
+      lu=""
+      vendor=""
+      tmpin=$(echo $hwpath | cut -d. -f1)
+### Changed 6/15/99 J.Semroc - to improve Performance reuse existing data
+###   inst=$(ioscan -kf | grep "^ext_bus" | grep " ${tmpin} " \
+###               | grep -v fcpdev | awk '{print $2}')
+      inst=$(grep $hwpath $io_scan_out | grep disk | awk '{print $NF}' \
+                 | cut -d: -f2)
+### end of Change
+      #scsi_addr=$(echo $hwpath | cut -d. -f2)
+      #unit_addr=$(echo $hwpath | cut -d. -f3)
+      #physvol="/dev/dsk/c${inst}t${scsi_addr}d${unit_addr}"
+      #rphysvol="/dev/rdsk/c${inst}t${scsi_addr}d${unit_addr}"
+      #physvol=$(ioscan -kfnH ${hwpath} | awk ' BEGIN {FS=" "; RS=" "} /dev\/dsk/ {printf("%s", $1)}')
+      #rphysvol=$(ioscan -kfnH ${hwpath} | awk ' BEGIN {FS=" "; RS=" "} /dev\/rdsk/ {printf("%s", $1)}')
+      #block_major=$(grep "$hwpath" $io_scan_out | grep disk | awk -F: '{print $6}')
+      #char_major=$(grep "$hwpath" $io_scan_out | grep disk | awk -F: '{print $7}')
+      #block_minor=$(grep "$hwpath" $io_scan_out | grep disk | awk -F: '{print $8}')
+      #Debug "  block_major = ${block_major}"
+      #Debug "  char_major  = ${char_major}"
+      #Debug "  block_minor = ${block_minor}"
+
+      scsi_addr=$(echo $hwpath | awk -F. '{print $(NF-1)}')
+      unit_addr=$(echo $hwpath | awk -F. '{print $NF}')
+      physvol="/dev/dsk/c${inst}t${scsi_addr}d${unit_addr}"
+      rphysvol="/dev/rdsk/c${inst}t${scsi_addr}d${unit_addr}"
+
+      Debug "Physical volume ${physvol}"
+      Debug "  tmpin=${tmpin}"
+      Debug "  inst=${inst} scsi_addr=${scsi_addr} unit_addr=${unit_addr}"
+
+      XP256=$(grep ${hwpath} ${io_scan_out} | weed_targets | awk -F: '{print $18}')
+      if [[ ${XP256} = *OPEN* ]] 
+      then
+        print -n "X"
+        if [[ -f /usr/contrib/bin/inquiry256 ]]
+        then 
+          inqcmd="/usr/contrib/bin/inquiry256"
+        else
+          inqcmd="${where}/inquiry256"
+        fi
+        Debug "XP256 disc array found! ${XP256} ${hwpath}"
+        Debug "inqcmd=${inqcmd}"
+        info=$(diskinfo -v ${rphysvol}  2>&1)
+        case "$info" in
+          *"No such file or directory"* )
+            Debug "  diskinfo reports No such file or directory on $physvol"
+            #echo "$Inq" | \
+            #   awk -F"+" '{printf("%55s\n"),$1}' \
+            #      >> /tmp/sysinfo.$$.inq256
+            rphysvold=$rphysvol
+            print "$rphysvold   No such file or directory" \
+                  >> /tmp/sysinfo.$$.inq256
+          ;;
+          *"No such device or address"* )
+            Debug "  diskinfo reports No such device or address on $physvol"
+           #echo "$Inq" | \
+           #   awk -F"+" '{printf("%55s\n"),$1}' \
+           #      >> /tmp/sysinfo.$$.inq256
+            rphysvold=$rphysvol
+            print "$rphysvold   No such device or address" \
+                  >> /tmp/sysinfo.$$.inq256
+          ;;
+          * )
+          Debug "  found a device (no diskinfo error)...so follow it"
+          Inq=$(${inqcmd} ${rphysvol} 2>&1 )
+          psize=$(echo $info | sed -e 's/^.*size: //' -e 's/ .*$//')
+          psize_mb=$(expr ${psize} / 1024)
+          product1=$(echo $info | sed -e 's/^.*product id: //' -e 's/ .*$//')
+          echo "$Inq+$product1+$psize_mb" | \
+             awk -F"+" '{printf("%55s%16s%8s\n"),$1,$2,$3}' \
+                >> /tmp/sysinfo.$$.inq256
+        esac
+      fi  # end of XP256
+
+      case ${XP256} in
+
+        *C1300* | *C2300* | *C3400* )
+          Debug " found an Nike disk array at $hwpath"
+          print -n "N" 
+          #f_get_array_data
+        ;;
+        *C2430* )
+          Debug " found a Cascade disk array at $hwpath"
+          print -n "C" 
+          #f_get_array_data
+        ;;
+        *C5447A* | *C3586A* )
+          # C5447A is 12H   C3586A is 12
+          Debug " found a AutoRaid disk array at $hwpath"
+          print -n "A" 
+          #f_get_array_data
+        ;;
+        *A5277A* )
+          Debug " found a FC60 disk array at $hwpath"
+          print -n "F" 
+          #f_get_array_data
+        ;;
+        * )
+          Debug " found a jbod disk at $hwpath"
+          print -n "." 
+          #f_get_array_data
+        ;;
+      esac
+
+
+# Run pvdisplay to get the disk information
+      pvdisplay -v ${physvol} > /tmp/sysinfo.$$.pvdisp 2>&1
+# Check for alternate link so that we don't try to access it
+      Debug "  Checking if this is an alternate link"
+      grep "Using Primary Link" /tmp/sysinfo.$$.pvdisp > /dev/null 2>&1
+      detect_alt_link=$?
+      if ((detect_alt_link == 0))
+      then
+        Debug "  this IS an alternate link, so skip"
+        alt_link=true
+      elif [[ "${rphysvol}" = "" ]] then
+        Debug "  no physvol found for $hwpath"
+        if ((! (($exit_code)) ))
+        then
+          exit_code=${WARN}
+        fi
+        print "WARNING (${sysname}):No device file found for $hwpath" >> \
+                     /tmp/sysinfo.$$.errwarn
+      else
+        Debug "  this is NOT an alternate link, so query disk"
+        hwpathd=${hwpath} # for display purposes
+        #info=$(diskinfo -v /dev/rdsk/c${inst}t${scsi_addr}d${unit_addr} 2>&1)
+        info=$(diskinfo -v ${rphysvol}  2>&1)
+        Debug "info= ${info}"
+        case "$info" in
+          *"Device busy"* )
+            Debug "  diskinfo reports Device Busy Error on $rphysvol"
+            lu=$(grep ${hwpath} $io_scan_out | grep -v target \
+                  | awk '{print $2}')
+            vendor1=$(ioscan -H $hwpath | tail -1 | awk '{print $3}')
+            vendor=${vendor1}
+            product1=$(ioscan -H $hwpath | tail -1 | awk '{print $NF}')
+            product=${product1}
+            print "${physvol}${lu}${hwpathd} ${vendor}${product}" \
+                   >> /tmp/sysinfo.$$.physinfo
+            Debug "  ${physvol}${lu}${hwpathd}${vendor}${product}" 
+          ;;
+          *"No such file or directory"* )
+            Debug "  diskinfo reports No such file or directory on $physvol"
+            lu="n/f"
+            vendor1=$(ioscan -H $hwpath | tail -1 | awk '{print $3}')
+            vendor=${vendor1}
+            product1=$(ioscan -H $hwpath | tail -1 | awk '{print $NF}')
+            product=${product1}
+            print "${physvol}${lu}${hwpathd} ${vendor}${product}" \
+                     >> /tmp/sysinfo.$$.physinfo
+          ;;
+          *"No such device or address"* )
+            Debug "  diskinfo reports No such device or address on $physvol"
+            lu="n/f"
+            vendor1=$(ioscan -H $hwpath | tail -1 | awk '{print $3}')
+            vendor=${vendor1}
+            product1=$(ioscan -H $hwpath | tail -1 | awk '{print $NF}')
+            product=${product1}
+            print "${physvol}${lu}${hwpathd} ${vendor}${product}" \
+                     >> /tmp/sysinfo.$$.physinfo
+          ;;
+          * )
+            Debug "  found a device (no diskinfo error)...so follow it"
+        
+          #lu=$(grep ${hwpath} $io_scan_out | grep -v target | awk '{print $2}')
+### Changed 6/15/99 J.Semroc - to improve Performance reuse existing data
+###       lu=$(ioscan -kfCdisk | grep "${hwpath}" | awk '{print $2}')
+          lu=$(grep ${hwpath} $io_scan_out | grep -v target | grep disk | \
+               awk -F: '{print $13}')
+### end of Change
+          Debug "LU = ${lu}"
+          ludebug=$(grep ${hwpath} $io_scan_out | grep -v target )
+          Debug "ludebug= ${ludebug}"
+          psize=$(echo $info | sed -e 's/^.*size: //' -e 's/ .*$//')
+          psize_mb=$(expr ${psize} / 1024)
+          product1=$(echo $info | sed -e 's/^.*product id: //' -e 's/ .*$//')
+  
+          vendor1=$(echo $info | sed -e 's/^.*vendor: //' -e 's/ .*$//')
+          vendor=${vendor1}
+          rev_level=$(echo $info | sed -e 's/^.*rev level: //' -e 's/ .*$//')
+          #rev_level=$(echo $info | grep "rev level" \
+          #       | awk '{print $3}')
+          product=${product1}
+          if [[ "${product1}" != "CD-ROM" ]] && [[ "${product1}" != "DVD-ROM" ]]
+          then
+            ((total_p_mb=total_p_mb + psize_mb))
+            ((pcount=pcount + 1))
+          fi
+          Debug "    vendor1=$vendor1"
+          Debug "    product=$product"
+          Debug "    rev_level=$rev_level"
+          Debug "    psize=$psize"
+          Debug "    psize_mb=$psize_mb"
+#
+# check for bootable disk
+#
+          Debug "  Checking for Bootable PV"
+          lifls ${physvol} 2> /dev/null | grep -i isl > /dev/null 2>&1
+          if [ $? -eq 0 ]
+          then   
+            Debug "    Found Bootable PV at ${physvol}"
+            bootable_pv="Y"
+            lifls ${rphysvol} | grep AUTO > /dev/null 2>&1
+            if [ $? -eq 0 ]
+            then   
+              Debug "    Found auto_string=$auto_string"
+              auto_string=$(lifcp ${rphysvol}:AUTO -)
+              print "${physvol}${hwpathd}   ${auto_string}" >> \
+                       /tmp/sysinfo.$$.boot
+            else
+              Debug "    Did not find auto_string."
+            fi
+          else 
+            Debug "    Did not find Bootable PV on ${physvol}"
+            bootable_pv="N"
+          fi
+          if grep "find the volume group" /tmp/sysinfo.$$.pvdisp > /dev/null
+          then
+            Debug "  NON-LVM   /dev/dsk/c${inst}t${scsi_addr}d${unit_addr}" 
+            Debug "    id as::${vendor}${product}${psize_mb} Mbytes." 
+            #lu="nlv"
+          elif grep "path does not correspond" /tmp/sysinfo.$$.pvdisp > /dev/null
+          then
+            Debug "Specified path not found! ${rphysvol}"
+          else            
+            Debug "  LVM disk"
+### Changed 6/15/99 J.Semroc - to improve Performance reuse existing data
+###         lu=$(ioscan -kfCdisk | grep " ${hwpath}" | awk '{print $2}')
+            lu=$(grep ${hwpath} $io_scan_out | grep -v target | grep disk | \
+                 awk -F: '{print $13}')
+### end of Change
+            if [[ "${vendor1}" = "EMC" ]]  # && ((detect_alt_link == 0))
+            then
+              EMC_found=1
+### Correction 6/15/99  J.Semroc - physvol was misspelled
+              Debug " Found EMC  at ${physvol}."
+              Debug "   detect_alt_link = ${detect_alt_link}"
+              bblvols=$(pvdisplay -v ${physvol} |  \
+                      awk ' /current/ {print $3}' | sort | uniq | \
+                      grep -v "/dsk/")
+              for bblvol in $bblvols
+              do
+                bblock=$(lvdisplay $bblvol | awk ' /Bad block/ {print $3}')
+                Debug "    bblvol=  $bblvol"
+                Debug "    bblock=  $bblock"
+                if [[ "${bblock}" != "NONE" ]]
+                then
+                  if ((! (($exit_code)) ))
+                  then
+                    exit_code=${WARN}
+                  fi
+                  print "WARNING (${sysname}):EMC w/ LVM bad block enabled on ${bblvol}" >> /tmp/sysinfo.$$.errwarn
+                fi
+              done
+            fi
+            if (($PMAP)) 
+            then
+              Debug "  Begin PHYSICAL TO LOGICAL Mapping"
+              alt_link=""
+              if  (($osmajor == 11))
+              then
+                # PVdisplay looks different on 10.20 (MWR)
+                alt_link=$(grep "^PV Name" /tmp/sysinfo.$$.pvdisp | \
+                   grep "Alternate Link" | awk '{printf "%s ", $3}' )
+                Debug "  =11     alt_link=$alt_link"
+              elif (($osmajor == 10)) && (($osminor >= 20))
+              then
+                # PVdisplay looks different on 10.20 (MWR)
+                alt_link=$(grep "^PV Name" /tmp/sysinfo.$$.pvdisp | \
+                   grep "Alternate Link" | awk '{printf "%s ", $3}' )
+                Debug "  >=10.20     alt_link=$alt_link"
+              else 
+                #less than 10.20
+                alt_link=$(grep "^   PV Name" /tmp/sysinfo.$$.pvdisp | \
+                   grep "Alternate Link" | awk '{printf "%s ", $3}' )
+                Debug "  <10.20   alt_link=$alt_link"
+              fi
+              if  [ "$alt_link" = "" ]
+              then
+                alt_link="None"
+              fi
+              Debug "    alt_link=$alt_link"
+              print "${physvol}                           ${alt_link}" \
+                 >> /tmp/sysinfo.$$.pmap
+              sed '1,15d;/current/d;/Status/d;/free/d;' \
+                /tmp/sysinfo.$$.pvdisp | \
+                sed '/^$/d;/---/d;/LV/d; s/\/dev/    \/dev/' \
+                >> /tmp/sysinfo.$$.pmap
+            fi   # end of PMAP
+          fi
+          print -n "${physvol}${lu}${hwpathd} ${vendor}${product}" >> \
+                      /tmp/sysinfo.$$.physinfo
+          print " ${psize_mb} ${bootable_pv}" >> \
+                      /tmp/sysinfo.$$.physinfo
+          ;;
+        esac
+        #print -n "."
+      fi
+    done # end of hwpath loop
+    # end of 10.x system
+  else      
+    Debug "must be a 9.x system"
+    pvol=$(vgdisplay -v 2>&1 | grep "^   PV Name" | grep -v "Alternate Link" \
+           | sort | awk '{printf "%s ", $3}' )
+    ioscan -kfCdisk | sort > /tmp/sysinfo.$$.io_disk
+    #now force access to disks.
+    for physvol in ${pvol}                        # loop through all phys vols
+    do
+      hwpath=$(lssf ${physvol} | awk '{printf "%s ", $(NF -1)}')
+      hwpathd=${hwpath} # for display purposes
+      disktype=$(echo ${physvol} | sed s/dsk/rdsk/)
+      diskinfo -v ${disktype} > /tmp/sysinfo.$$.diskinfo 2>&1
+      if (($?))
+      then
+        print "ERROR:${sysname}: diskinfo reports an error on ${physvol}."
+        >> /tmp/sysinfo.$$.errwarn
+        exit_code=${SYS_ERROR}
+      else
+        vendor=$(grep "vendor" /tmp/sysinfo.$$.diskinfo \
+                 | awk '{printf "%s ", $2}')
+        product1=$(grep "product id" /tmp/sysinfo.$$.diskinfo \
+                 | awk '{printf "%s ", $3}')
+        type=$(grep "type" /tmp/sysinfo.$$.diskinfo \
+                 | cut -b22- )
+        psize=$(grep "size" /tmp/sysinfo.$$.diskinfo \
+                 | awk '{printf "%s ", $2}')
+        psize_mb=$(expr ${psize} / 1024)
+        lu=$(grep " ${hwpath}" /tmp/sysinfo.$$.io_disk | awk '{print $2}')
+        rev_level=$(grep "rev level" /tmp/sysinfo.$$.diskinfo \
+                 | awk '{print $3}')
+        product=${product1}
+        if [[ "${product1}" != "CD-ROM" ]] && [[ "${product1}" != "DVD-ROM" ]]
+        then
+          ((total_p_mb=total_p_mb + psize_mb))
+          ((pcount=pcount + 1))
+        fi
+        if (($PMAP))
+        then
+          pvdisplay -v ${physvol} > /tmp/sysinfo.$$.pvdisp 2>&1
+          alt_link=""
+          alt_link=$(grep "^   PV Name" /tmp/sysinfo.$$.pvdisp | \
+               grep "Alternate Link" | awk '{printf "%s ", $3}' )
+          #alt_link="/dev/dsk/cxtxdx"
+          if  [ "$alt_link" = "" ]
+          then
+            alt_link="None"
+          fi
+          print "${physvol}                           ${alt_link}" \
+             >> /tmp/sysinfo.$$.pmap
+          sed '1,15d;/current/d;/Status/d;/free/d;' /tmp/sysinfo.$$.pvdisp | \
+            sed '/^$/d;/---/d;/LV/d; s/\/dev/    \/dev/' \
+            >> /tmp/sysinfo.$$.pmap
+        fi
+        Debug "\ndisktype=$disktype"
+        Debug "hwpath=$hwpath"
+        Debug "vendor=$vendor"
+        Debug "product=$product"
+        Debug "psize=$psize"
+        Debug "psize_mb=$psize_mb"
+        Debug "lu=$lu"
+        Debug "type=$type"
+        Debug "total_p_mb=$total_p_mb"
+        Debug "pcount=$pcount"
+        Debug "rev_level=$rev_level"
+        Debug "alt_link=$alt_link"
+        print -n "."
+        print "${physvol}${lu}${hwpathd}${vendor}${product}${psize_mb}" >> /tmp/sysinfo.$$.physinfo
+      fi
+    done
+  fi # end of 9.x
+
+  print "                             Rev" > /tmp/sysinfo.$$.disk_rev
+  print "             HW Path        Level         Serial Number      LUN" \
+          >> /tmp/sysinfo.$$.disk_rev
+  print "   ---------------------    -----    ---------------------   ----" \
+          >> /tmp/sysinfo.$$.disk_rev
+  
+  if (( ${cstm_ok} == 0 ))
+  then
+  awk '
+    /Hardware path/ {path = $3; next;}
+    /Product Id/   {prod = $3; vend = $NF; next;}
+    /Firmware Rev/ {fw = $NF; next;}
+    /Logical Unit/ {lu = $NF; next;}
+    /Serial Number/ {sn = $3; next;}
+    /Capacity/ {cap = $NF; printf("%24s %8s %24s %6s\n",
+       path,fw,sn,lu);}' $cstm_disk_out >> /tmp/sysinfo.$$.disk_rev
+  print "" >> /tmp/sysinfo.$$.disk_rev
+  fi
+
+  if (($EMC_found))
+  then
+    Debug "calling EMC query routine"
+    #query_EMC_disks
+
+  fi
+#
+# check for boot path settings
+#
+  if [[ -f /usr/sbin/setboot ]]
+  then
+    pri_boot_path=$(/usr/sbin/setboot | grep Primary | awk '{print $4}')
+    alt_boot_path=$(/usr/sbin/setboot | grep Alternate | awk '{print $4}')
+    auto_boot=$(/usr/sbin/setboot | grep Autoboot | awk '{print $3}')
+    auto_search=$(/usr/sbin/setboot | grep Autosearch | awk '{print $3}' )
+    print "" >> /tmp/sysinfo.$$.boot
+    print "Primary Boot Path   = ${pri_boot_path}" >> /tmp/sysinfo.$$.boot
+    print "Alternate Boot Path = ${alt_boot_path}" >> /tmp/sysinfo.$$.boot
+    print "Autoboot            = ${auto_boot}"     >> /tmp/sysinfo.$$.boot
+    print "Autosearch          = ${auto_search}"   >> /tmp/sysinfo.$$.boot
+  fi
+  print  "\n" >> /tmp/sysinfo.$$.boot
+
+# now print out totals
+### Changed 6/16/99 J.Semroc sort LU numerically
+###  sort -k2. -o /tmp/sysinfo.$$.physinfos /tmp/sysinfo.$$.physinfo
+  sort -k2n -o /tmp/sysinfo.$$.physinfos /tmp/sysinfo.$$.physinfo
+### end of Change
+  rm -f /tmp/sysinfo.$$.physinfo 2>&1
+  print
+# print     "" >> /tmp/sysinfo.$$.physinfoh
+  print  -n "                         " >> /tmp/sysinfo.$$.physinfoh
+  print     "                                               Size" >> /tmp/sysinfo.$$.physinfoh
+  print  -n " Volume Name      LU#         " >> /tmp/sysinfo.$$.physinfoh
+  print     "     H/W Path     Vendor  /  Model        (MB)  B" >> /tmp/sysinfo.$$.physinfoh
+  print  -n " --------------   ---       " >> /tmp/sysinfo.$$.physinfoh
+  print     " ----------------  --------------------    ------ -" >> /tmp/sysinfo.$$.physinfoh
+  print     "                                                                       ======" >> /tmp/sysinfo.$$.physinfot
+  print  -n "                                                   Total (MB)" >> /tmp/sysinfo.$$.physinfot
+  print     "     ${total_p_mb}" >> /tmp/sysinfo.$$.physinfot
+### Changed 6/15/99 J.Semroc - generated the following error
+###  "cat: Cannot use /tmp/sysinfo.14950.physinfod as both input and output."
+###  cat /tmp/sysinfo.$$.physinfo? > /tmp/sysinfo.$$.physinfod
+  cat /tmp/sysinfo.$$.physinfo? > /tmp/sysinfo.$$.PHYSINFOD
+  mv /tmp/sysinfo.$$.PHYSINFOD /tmp/sysinfo.$$.physinfod
+### end of Change
+  print "B = Bootable Disk" >> \
+             /tmp/sysinfo.$$.physinfod
+  print "n/v = Not Valid, n/f = Not Found\n" >> \
+             /tmp/sysinfo.$$.physinfod
+  f_display_file /tmp/sysinfo.$$.physinfod
+  print "" >> /tmp/sysinfo.$$.physwarn
+  f_display_file /tmp/sysinfo.$$.physwarn
+  f_display_file /tmp/sysinfo.$$.disk_rev
+  if [[ -f  /tmp/sysinfo.$$.inq256 ]]
+  then
+    f_display_file /tmp/sysinfo.$$.256header
+    print "" >> /tmp/sysinfo.$$.inq256
+    f_display_file /tmp/sysinfo.$$.inq256
+  fi
+
+  if [[ -f  /tmp/sysinfo.$$.array ]]
+  then
+    f_display_file /tmp/sysinfo.$$.arrayheader
+    print "\n\n" >> /tmp/sysinfo.$$.array
+    f_display_file  /tmp/sysinfo.$$.array
+  fi
+
+  f_display_file /tmp/sysinfo.$$.boot
+  if (($PMAP))
+  then
+    f_display_file /tmp/sysinfo.$$.pmap
+  fi
+  
+}                # end of f_get_physical_disk_data
+
+#===================================================================
+# f_get_ioscan_data
+#===================================================================
+function f_get_ioscan_data
+{
+  Debug "  calling check_if_scan_done"
+  check_if_scan_done io_scan
+  GREP_V_ARGS="-eboot_console $GREP_V_ARGS"
+  if (($HTML))
+  then
+    print "
" >> /tmp/sysinfo.$$.ioout + print "" >> /tmp/sysinfo.$$.ioout + print "

" >> /tmp/sysinfo.$$.ioout + print "

TOP

" >> /tmp/sysinfo.$$.ioout + print "" >> /tmp/sysinfo.$$.ioout + print "I/O Configuration Data

" >> /tmp/sysinfo.$$.ioout + print "
"   >> /tmp/sysinfo.$$.ioout
+  else
+    print "I/O CONFIGURATION DATA" >> /tmp/sysinfo.$$.ioout
+    print "======================" >> /tmp/sysinfo.$$.ioout
+  fi
+  print "" >> /tmp/sysinfo.$$.ioout
+  print "              H/W Path       Driver  Identifier String" >>/tmp/sysinfo.$$.ioout
+  print "              --------       ------  -----------------" >>/tmp/sysinfo.$$.ioout
+ 
+  Debug "  `cat ${io_scan_out}`"
+  if (( ${osmajor} >= 10 ))
+  then
+    Debug "found 10.x or later"
+    Debug "sending $io_scan_out to formatter"
+    Debug "  GREP_V_ARGS = $GREP_V_ARGS"
+
+    cat $io_scan_out | weed_targets | formatter | \
+        grep -F -v $GREP_V_ARGS >> /tmp/sysinfo.$$.ioout
+  else
+    Debug "found 9.x"
+    cp ${io_scan_out} /tmp/sysinfo.$$.ioout
+  fi
+  Debug "  `cat /tmp/sysinfo.$$.ioout`"
+  
+  #rm -f $io_scan_tmp
+  print "" >>/tmp/sysinfo.$$.ioout
+  f_display_file /tmp/sysinfo.$$.ioout
+
+}                # end of f_get_ioscan_data
+
+#===================================================================
+# f_get_volume_group_data
+#        This function retrieves volume group data using vgdisplay
+#        and by scannig /etc/lvmconf for config files.
+#===================================================================
+function f_get_volume_group_data
+{
+  Debug "Beginning VOLUME GROUP check."
+  # first print header information
+
+  if (($HTML))
+  then
+    print "
" >> /tmp/sysinfo.$$.vginfoh + print "" >> /tmp/sysinfo.$$.vginfoh + print "

" >> /tmp/sysinfo.$$.vginfoh + print "

TOP

" >> /tmp/sysinfo.$$.vginfoh + print "" >> /tmp/sysinfo.$$.vginfoh + print "Volume Group Data

" >> /tmp/sysinfo.$$.vginfoh + print "
"     >> /tmp/sysinfo.$$.vginfoh
+  else
+    print                     >> /tmp/sysinfo.$$.vginfoh
+    print "VOLUME GROUP DATA" >> /tmp/sysinfo.$$.vginfoh
+    print "=================" >> /tmp/sysinfo.$$.vginfoh
+  fi
+  Debug "  calling check_if_scan_done"
+  check_if_scan_done lvm_scan
+
+  vgdisplay -v > /tmp/sysinfo.$$.vgout 2>&1
+  #MWR fix when PVG-strict policies used
+  #all_volgroups=$(grep "VG Name" /tmp/sysinfo.$$.vgout | awk '{print $3}' | sort)
+  all_volgroups=$(grep -v "PVG Name" /tmp/sysinfo.$$.vgout | \
+     grep "VG Name" | awk '{print $3}' | sort)
+  all_logvols=$(grep "LV Name" /tmp/sysinfo.$$.vgout | awk '{print $3}' | sort)
+  #Debug "all_volgroups=\n$all_volgroups"
+  #Debug "all_logvols=\n$all_logvols"
+  
+  print -n "scanning volume groups "
+  for volgroup in ${all_volgroups}
+  do
+    Debug "Volumegroup = $volgroup"
+    vgdisplay $volgroup > /tmp/sysinfo.$$.volgroup 2>&1
+    #Debug "vgdisplay=\n`cat /tmp/sysinfo.$$.volgroup`"
+    ((vgcount=vgcount + 1))
+    CURLV=$(grep "Cur LV" /tmp/sysinfo.$$.volgroup | awk '{print $3}')
+    CURPV=$(grep "Cur PV" /tmp/sysinfo.$$.volgroup | awk '{print $3}')
+    AllocPe=$(grep "Alloc PE" /tmp/sysinfo.$$.volgroup | awk '{print $3}')
+    FreePe=$(grep "Free PE" /tmp/sysinfo.$$.volgroup | awk '{print $3}')
+    TotalPe=$(grep "Total PE" /tmp/sysinfo.$$.volgroup | awk '{print $3}')
+    PeSize=$(grep "PE Size" /tmp/sysinfo.$$.volgroup | awk '{print $4}')
+    ((AllocMb = AllocPe * PeSize))
+    ((FreeMb = FreePe * PeSize))
+    ((TotalMb = TotalPe * PeSize))
+    ((TotalAllocMb = TotalAllocMb + AllocMb))
+    ((TotalFreeMb = TotalFreeMb + FreeMb))
+    ((SystemTotalMb = SystemTotalMb + TotalMb))
+    Debug "  Physical Extent Size = ${PeSize}"
+    Debug "  Number of Allocated Physical Extents = ${AllocPe}"
+    Debug "  Free Physical Extents = ${FreePe}"
+    Debug "  Total Physical Extents = ${TotalPe}"
+    Debug "  Allocated Mb = ${AllocMb}"
+    Debug "  Free Mb = ${FreeMb}"
+    Debug "  Total Mb = ${TotalMb}"
+    Debug "  Total Allocated Mb = ${TotalAllocMb}"
+    Debug "  Total Free Mb = ${TotalFreeMb}"
+    Debug "  System Total Mb = ${SystemTotalMb}"
+    print -n "."
+    print -n "${volgroup}${CURLV}${CURPV}${PeSize}" >> /tmp/sysinfo.$$.vginfos
+    print    "${AllocMb}${FreeMb}${TotalMb}" >> /tmp/sysinfo.$$.vginfos
+  done
+  print  
+  Debug "Checking for vgcount = maxvgs"
+  if [[ -z $maxvgs ]]
+  then
+    maxvgs=$(echo 'maxvgs/D'|adb -k $kernel /dev/kmem \
+             | tail -1 | awk '{print $2}')
+  fi
+  Debug " vgcount = $vgcount"
+  Debug " maxvgs = $maxvgs"
+  if ((vgcount == maxvgs))
+  then
+    if ((! (($exit_code)) ))
+    then
+      exit_code=${WARN}
+    fi
+    print "WARNING (${sysname}): number of volume groups is at maximum."\
+           >> /tmp/sysinfo.$$.errwarn
+  fi
+  
+# now check for a cfg backup file for each volume group.
+
+  print -n "scanning config files "
+  Debug "Checking vg config files"
+  for volgroup in ${all_volgroups}
+  do
+    Debug "  volgroup ${volgroup}"
+    vg=$(echo ${volgroup} | awk -F/ '{print $3}')
+    print -n "."
+    if [ -f /etc/lvmconf/${vg}.conf ]
+    then
+      # this is a kludge. if file is less than 6 months old then 
+      # ls -l prints the time, so scan for a colon.
+      ls -l /etc/lvmconf/${vg}.conf | awk '{print $8}' | grep ":" > /dev/null
+      if (($?))
+      then
+        if ((! (($exit_code)) ))
+        then
+          exit_code=${WARN}
+        fi
+        print "WARNING (${sysname}): vgconfig file > 6 months old for /etc/lvmconf/${vg}.conf"\
+               >> /tmp/sysinfo.$$.errwarn
+      fi
+    else
+      if ((! (($exit_code)) ))
+      then
+        exit_code=${WARN}
+      fi
+      print -n "WARNING (${sysname}): no config " >> /tmp/sysinfo.$$.errwarn
+      print    "file found for volume group ${vg}." >> /tmp/sysinfo.$$.errwarn
+      if ((! (($exit_code)) ))
+      then
+        exit_code=${WARN}
+      fi
+    fi
+  done
+  print  ""
+  f_display_file /tmp/sysinfo.$$.novgcfg 
+  print    "" >> /tmp/sysinfo.$$.vginfoh
+  print    " Volume               Logical  Physical  Extent   MBs of Physical Space" >> /tmp/sysinfo.$$.vginfoh
+  print    " Group                Volumes  Volumes  Size(MB)  Alloc    Free   Total"  >> /tmp/sysinfo.$$.vginfoh
+  print    " ---------            -------  --------  ------   -----    ----   -----"  >> /tmp/sysinfo.$$.vginfoh
+  print    "                                                  =====    ====   =====" >> /tmp/sysinfo.$$.vginfot
+  print -n "                                        Totals " >> /tmp/sysinfo.$$.vginfot
+  print     "${TotalAllocMb}${TotalFreeMb}${SystemTotalMb}" >> /tmp/sysinfo.$$.vginfot
+  cat /tmp/sysinfo.$$.vginfo? > /tmp/sysinfo.$$.vginfod
+  f_display_file /tmp/sysinfo.$$.vginfod
+
+} # end of f_get_volume_group_data
+
+#===================================================================
+# f_get_logical_volume_data
+#        This function uses lvdisplay to scan logical volumes.
+#        It retrieves information about the logvol and also checks
+#        for stale mirrors (if used).
+#===================================================================
+function f_get_logical_volume_data
+{
+  Debug "Beginning LOGICAL DISK check."
+  # first print out header information
+  
+  if (($HTML))
+  then
+    print "
" >> /tmp/sysinfo.$$.lvinfoh + print "" >> /tmp/sysinfo.$$.lvinfoh + print "

" >> /tmp/sysinfo.$$.lvinfoh + print "

TOP

" >> /tmp/sysinfo.$$.lvinfoh + print "" >> /tmp/sysinfo.$$.lvinfoh + print "Logical Volume And Extent Data

" >> /tmp/sysinfo.$$.lvinfoh + print "
"           >> /tmp/sysinfo.$$.lvinfoh
+  else
+    print                                     >> /tmp/sysinfo.$$.lvinfoh
+    print  "LOGICAL VOLUME AND EXTENT DATA"   >> /tmp/sysinfo.$$.lvinfoh  
+    print  "=============================="   >> /tmp/sysinfo.$$.lvinfoh
+  fi
+  Debug "  calling check_if_scan_done"
+  check_if_scan_done lvm_scan
+  
+  print -n "scanning logical volumes "
+  if [[ ! -f /tmp/sysinfo.$$.vgout ]]
+  then
+    vgdisplay -v > /tmp/sysinfo.$$.vgout 2>&1
+    all_volgroups=$(grep "VG Name" /tmp/sysinfo.$$.vgout | awk '{print $3}' | sort)
+    all_logvols=$(grep "LV Name" /tmp/sysinfo.$$.vgout | awk '{print $3}' | sort)
+  fi
+  for logvol in ${all_logvols}
+  do
+    Debug "lvdisplay of ${logvol}"
+    lvol_major=$(ls -l $logvol | awk '{print $5}')
+    lvol_minor=$(ls -l $logvol | awk '{print $6}')
+    Debug "  major= $lvol_major minor=$lvol_minor"
+    lvdisplay -v ${logvol} > /tmp/sysinfo.$$.logical 2>&1
+    STATUS=$(grep "LV Status" /tmp/sysinfo.$$.logical | awk '{print $3}')
+    LVSize=$(grep "LV Size" /tmp/sysinfo.$$.logical | awk '{print $4}')
+    LogicalExtents=$(grep "Current LE" /tmp/sysinfo.$$.logical \
+                  | awk '{print $3}')
+    StaleExtents=$(grep stale /tmp/sysinfo.$$.logical | wc -l)
+    MirrorCopies=$(grep "Mirror copies" /tmp/sysinfo.$$.logical \
+                  | awk '{print $3}')
+    Consistency=$(grep "Consistency Recovery" /tmp/sysinfo.$$.logical \
+                  | awk '{print $3}')
+  
+  
+    ((total_l_mb = total_l_mb + LVSize))
+    ((MirrorMb = LVSize * MirrorCopies))
+    ((TotalMirrorMb = TotalMirrorMb + MirrorMb))
+    
+  # check to make sure lvol has extents allocated to it
+    if (( $LogicalExtents > 0 ))                 
+    then
+### Changed 6/15/99 J.Semroc - grep for lvol1 would also match lvol10, lvol11...
+###   MOUNT=$(cat /etc/mnttab | grep ${logvol}  | cut -f 2 -d" ")
+      MOUNT=$(cat /etc/mnttab | grep "${logvol} " | cut -f 2 -d" ")
+### end of Change
+      if [[ ${swap_devs} = *${logvol}* ]]
+      then
+        MOUNT="(swapdisk)"
+      fi
+      ((lcount=lcount + 1))
+      if (( ${StaleExtents} > 0 ))        # we found stale extents
+      then
+        Debug " Found ${StaleExtents} STALE extents on ${logvol}!"
+        stale=1
+        ((StaleMB = (LVSize / LogicalExtents) * StaleExtents ))
+        ((TotalStaleMb = TotalStaleMb + StaleMB))
+        ((total_stale = $total_stale + $StaleExtents))
+      fi                                # end of stale extents
+    else                                # no extents allocated
+      if ((! (($exit_code)) ))
+      then
+        exit_code=$WARN
+      fi
+      zero_length=1
+      print -n "\nWARNING (${sysname}): Detected logical " >> /tmp/sysinfo.$$.errwarn
+      print "volume (${logvol}) with no extents!\n" >> /tmp/sysinfo.$$.errwarn
+    fi                                # end of check for allocated extents
+    Debug "  STATUS= ${STATUS}"
+    Debug "  LVSize= ${LVSize}"
+    Debug "  LogicalExtents = ${LogicalExtents}"
+    Debug "  StaleExtents = ${StaleExtents}"
+    Debug "  StaleMb= $StaleMB"
+    Debug "  TotalStaleMb= $TotalStaleMb "
+    Debug "  TotalMb checked= $total_l_mb"
+
+# now let's print out the results.
+    print -n "."
+    logvold=${logvol}                        # for display purposes only.
+    print "${logvold}${MOUNT}"               >> /tmp/sysinfo.$$.lvinfos
+    #print "${LogicalExtents}${StaleExtents}" >> /tmp/sysinfo.$$.lvinfos
+    #print "${MirrorCopies}${Consistency}"    >> /tmp/sysinfo.$$.lvinfos
+  done                                
+# end of loop for all log vols
+# now print out any errors or warnings.
+  f_display_file /tmp/sysinfo.$$.nolvext 
+# print    "" >> /tmp/sysinfo.$$.lvinfoh
+  print    "                                     "  >> /tmp/sysinfo.$$.lvinfoh
+  print -n " Volume Name                            " >> /tmp/sysinfo.$$.lvinfoh
+  print    "Mounted File System"  >> /tmp/sysinfo.$$.lvinfoh
+  print -n " --------------                         " >> /tmp/sysinfo.$$.lvinfoh
+  print -- "--------------------"  >> /tmp/sysinfo.$$.lvinfoh
+  print    "" >> /tmp/sysinfo.$$.lvinfos
+  cat /tmp/sysinfo.$$.lvinfo? > /tmp/sysinfo.$$.lvinfod
+  f_display_file /tmp/sysinfo.$$.lvinfod 
+
+  print "\nAllocation" > /tmp/sysinfo.$$.lvol_legend
+  print -n "  ns    non-strict                " >> /tmp/sysinfo.$$.lvol_legend
+  print    "PVG-s  PVG-strict                " >> /tmp/sysinfo.$$.lvol_legend
+  print -n "  ns-c  non-strict/contiguous     " >> /tmp/sysinfo.$$.lvol_legend
+  print    "PVG-c  PVG-strict/contiguous" >> /tmp/sysinfo.$$.lvol_legend
+  print -n "  s     strict                    " >> /tmp/sysinfo.$$.lvol_legend
+  print    "PVG-d  PVG-strict/distributed    " >> /tmp/sysinfo.$$.lvol_legend
+  print -n "  s-c   strict/contiguous         " >> /tmp/sysinfo.$$.lvol_legend
+  print    "PVG-p  PVG-strict/partially-distributed" >> /tmp/sysinfo.$$.lvol_legend
+  print "" >> /tmp/sysinfo.$$.lvol_legend
+
+
+# display using data from batch lvm_scan
+  print    "" > /tmp/sysinfo.$$.lvinfoh
+  print -n "                                 "  >> /tmp/sysinfo.$$.lvinfoh
+  print    "                                  Bad" >> /tmp/sysinfo.$$.lvinfoh
+  print -n "                                 "  >> /tmp/sysinfo.$$.lvinfoh
+  print    "  Size Stripes    Mirrors    LV   Block" >> /tmp/sysinfo.$$.lvinfoh
+  print -n " Volume Name                     "  >> /tmp/sysinfo.$$.lvinfoh
+  print    "  (MB)  # Size    #  Const  State Reloc  Alloc" >> /tmp/sysinfo.$$.lvinfoh
+  print -n " --------------                  "  >> /tmp/sysinfo.$$.lvinfoh
+  print    " ----- -------   ---------  ----- -----  -----"  >> /tmp/sysinfo.$$.lvinfoh
+  f_display_file /tmp/sysinfo.$$.lvinfoh
+  f_display_file $lvol_out_file
+  f_display_file /tmp/sysinfo.$$.lvol_legend
+  if (($BATCH))
+  then
+    print
+  fi
+
+}         # end of f_get_logical_volume_data
+
+
+#===================================================================
+# f_logical_to_physical
+#        This function produces a cross reference of physical disks
+#        for each logical volume.
+#===================================================================
+function f_logical_to_physical
+{
+  Debug "Beginning LOGICAL TO PHYSICAL Mapping."
+  
+  if [[ ! -f /tmp/sysinfo.$$.vgout ]]
+  then
+    Debug "/tmp/sysinfo.$$.vgout not found....building."
+    vgdisplay -v > /tmp/sysinfo.$$.vgout 2>&1
+    all_logvols=$(grep "LV Name" /tmp/sysinfo.$$.vgout | awk '{print $3}' |sort)
+  fi
+  if (($HTML))
+  then
+    print "
" >> /tmp/sysinfo.$$.l2p + print "" >> /tmp/sysinfo.$$.l2p + print "

" >> /tmp/sysinfo.$$.l2p + print "

TOP

" >> /tmp/sysinfo.$$.l2p + print "" >> /tmp/sysinfo.$$.l2p + print "Logical Volume To Physical Disk Mapping

" >> /tmp/sysinfo.$$.l2p + print "
"             >> /tmp/sysinfo.$$.l2p
+  else
+    print    "" >> /tmp/sysinfo.$$.l2p
+    print    "LOGICAL VOLUME TO PHYSICAL DISK MAPPING                 " >> /tmp/sysinfo.$$.l2p
+    print    "=========================================================" >> /tmp/sysinfo.$$.l2p
+  fi
+  print "Logical Volume" >> /tmp/sysinfo.$$.l2p
+  print " extents  Primary disk     Mirror Disk 1    Mirror Disk 2" >> /tmp/sysinfo.$$.l2p
+  print -- "---------------------------------------------------------" >> /tmp/sysinfo.$$.l2p
+  print -n "generating disk mappings "
+
+  for logvol in ${all_logvols}
+  do
+    Debug "  scanning ${logvol}"
+    lvdisplay -v ${logvol} > /tmp/sysinfo.$$.logical 2>&1
+    PhysicalDisks=$(awk '$1 ~ /\/dev\/dsk\/.*/ {printf "%s\n",$1}' \
+                     /tmp/sysinfo.$$.logical \
+                     | sed -e 's/\/dev\/dsk\///' | sort)
+    # output physical disk data for each logical volume
+    print    "${logvol}    " >> /tmp/sysinfo.$$.l2p
+#   print -n "     Disks = " >> /tmp/sysinfo.$$.l2p
+
+# count the number of physical disks found.
+    diskcount=0
+    for disk in ${PhysicalDisks}
+    do
+      ((diskcount = diskcount + 1))
+      Debug "    disk = $disk"
+    done
+    Debug "    found $diskcount physical disks"
+    lvdisplay -v $logvol | sed '1,/   LE/d' | awk '{print $2, $5, $8}' \
+                     | sort | grep "dev" | uniq -c | awk \
+                     '{printf "%6s %18s %16s %16s\n",$1,$2,$3,$4}' \
+                     >> /tmp/sysinfo.$$.l2p
+#   print "" >> /tmp/sysinfo.$$.l2p
+    print -n "."
+  done
+  print "" >> /tmp/sysinfo.$$.l2p
+  f_display_file /tmp/sysinfo.$$.l2p 
+  print  ""
+
+}        # end of f_logical_to_physical
+
+
+#===================================================================
+# f_filesystem_check
+#        This function checks mounted filesystems and reports 
+#        capcity information. It prints warnings if greater than
+#        95% full.
+#===================================================================
+
+function f_filesystem_check
+{
+  Debug "Beginning FILE SYSTEM check."
+
+  # first print out headers
+  if (($HTML))
+  then
+    print "
" >> /tmp/sysinfo.$$.bdf_outh + print "" >> /tmp/sysinfo.$$.bdf_outh + print "

" >> /tmp/sysinfo.$$.bdf_outh + print "

TOP

" >>/tmp/sysinfo.$$.bdf_outh + print "" >> /tmp/sysinfo.$$.bdf_outh + print "File System Data

" >> /tmp/sysinfo.$$.bdf_outh + print "
" >> /tmp/sysinfo.$$.bdf_outh
+  else
+    print    "" >> /tmp/sysinfo.$$.bdf_outh
+    print    "FILE SYSTEM DATA" >> /tmp/sysinfo.$$.bdf_outh
+    print    "================" >> /tmp/sysinfo.$$.bdf_outh
+  fi
+  print -n "scanning filesystems "
+
+  typeset -R10 kbytes used avail iused ifree
+  typeset -R5 percent_used
+  mnttab=$(cat /etc/mnttab | grep /dev  | awk '{printf "%s\n", $2}')
+  num_fs=$(bdf -il 2>&1 | grep "/dev/" | wc -l)
+  ((num_fs = num_fs -1))
+  Debug "Found $num_fs mounted file systems"
+# bdf -i | tail -${num_fs} > /tmp/sysinfo.$$.bdf_data 2>&1
+  for mounted in ${mnttab}
+  do
+    bdf -i ${mounted} 2>&1 | tail -1 > /tmp/sysinfo.$$.bdf_data 2>&1
+    if ( grep "No such file" /tmp/sysinfo.$$.bdf_data >/dev/null 2>&1 )
+    then 
+      Debug " bdf reports No such file or directory on ${mounted}"
+      print "WARNING (${sysname}): No such file or directory for ${mounted}" >>  /tmp/sysinfo.$$.errwarn
+    else
+    fs_type=$(cat /etc/mnttab | grep " ${mounted} " | awk '{printf "%s\n", $3}')
+    Debug "filesys = ${mounted}  fs_type = ${fs_type}"
+    if [[ $fs_type = vxfs ]]
+    then
+      mnt_lvol=$(cat /etc/mnttab | grep " ${mounted} " | awk '{printf "%s\n", $1}')
+      fs_ver=$(fstyp -v ${mnt_lvol} 2>&1 | grep version | awk '{printf "%s\n", $2}')
+      if [[ -f ${mounted}/lost+found/.fsadm ]]
+      then
+      vxfs_defrag=$(fsadm -F vxfs -E ${mounted} | grep \
+                  "extents 64 blks or larger" | awk '{print $NF}')
+      ((vxfs_frag = 100 - vxfs_defrag))
+      vxfs_pct="%"
+      else
+        Debug " ${mounted}/lost+found/.fsadm not found - no frag check"
+        no_lost=1
+        vxfs_frag="**"
+        vxfs_pct="*"
+      fi
+      Debug "fs_ver = ${fs_ver} vxfs_frag = ${vxfs_frag}"
+      if ( [[ $osmajor = 10 ]] && [[ $osminor > 10 ]] ) || [[ $osmajor = 11 ]]
+      then
+        if [[ $fs_ver < 3 ]]
+        then
+          Debug "Found vxfs version $fs_ver"
+          print "WARNING (${sysname}): Version ${fs_ver} of vxfs found on ${mounted}" >> /tmp/sysinfo.$$.errwarn
+        fi
+      fi
+    fi
+    Debug "  `cat /tmp/sysinfo.$$.bdf_data`"
+    kbytes=$(awk '{if (NF == 9) print $2; else print $1}' /tmp/sysinfo.$$.bdf_data) 
+    used=$(awk '{if (NF == 9) print $3; else print $2}' /tmp/sysinfo.$$.bdf_data)
+    if [[ $fs_type  != "cdfs" ]]
+    then
+      avail=$(awk '{if (NF == 9) print $4; else print $3}' /tmp/sysinfo.$$.bdf_data)
+      iused=$(awk '{if (NF == 9) print $6; else print $5}' /tmp/sysinfo.$$.bdf_data)
+      ifree=$(awk '{if (NF == 9) print $7; else print $6}' /tmp/sysinfo.$$.bdf_data)
+      #((percent_used = (used * 100) / (used + avail) ))
+      percent_used=$(echo $used $avail | \
+        awk '{ used = $1
+               avail = $2
+               pct_used =  ($1 * 100) / ($1 + $2)
+               printf("%d", pct_used)
+              } ' )
+
+      if (($percent_used > 95)) && [[ $fs_type  != "cdfs" ]]
+      then
+        if ((! (($exit_code)) ))
+        then
+          exit_code=${WARN}
+        fi
+        print "WARNING (${sysname}): ${mounted} has less than 5% free space left!" >> /tmp/sysinfo.$$.errwarn
+      fi
+    else #must be a cdrom, so don't calculate free space
+      avail=0
+      iused="-"
+      ifree="-"
+      percent_used="-"
+    fi
+    if [[ $fs_type != vxfs ]]
+    then
+      vxfs_frag="n/"
+      vxfs_pct="a"
+    fi
+    print "${mounted}" >> /tmp/sysinfo.$$.bdf_outs
+#   print "              ${fs_type} ${kbytes}${used}${avail}${percent_used}${iused}${ifree}" >> /tmp/sysinfo.$$.bdf_outs
+    print "              ${fs_type} ${kbytes}${used}${avail}${percent_used}     ${vxfs_frag}${vxfs_pct}" >> /tmp/sysinfo.$$.bdf_outs
+    print -n "."
+    fi
+  done
+  print "" >> /tmp/sysinfo.$$.bdf_outs
+  print -n "Mounted                               " >> /tmp/sysinfo.$$.bdf_outh
+  print -- "                    Extents" >> /tmp/sysinfo.$$.bdf_outh
+  print -n "Filesystems   Type     Kbytes      Used     Avail" >> /tmp/sysinfo.$$.bdf_outh
+# print    "   %      Iused     Ifree" >> /tmp/sysinfo.$$.bdf_outh
+  print    "   %    Fragmented" >> /tmp/sysinfo.$$.bdf_outh
+  print -n " ----------   ----     ------    ------    ------" >> /tmp/sysinfo.$$.bdf_outh
+  print -- "  ---   ----------" >> /tmp/sysinfo.$$.bdf_outh
+  cat /tmp/sysinfo.$$.bdf_out? > /tmp/sysinfo.$$.bdf_outd
+  if (( ${no_lost} == 1 ))
+  then
+    print -n "***  could not get fsadm lock - " >> /tmp/sysinfo.$$.bdf_outd
+    print "lost+found/.fsadm not found" >> /tmp/sysinfo.$$.bdf_outd
+    print "" >> /tmp/sysinfo.$$.bdf_outd
+  fi
+  f_display_file /tmp/sysinfo.$$.bdf_outd 
+  f_display_file /tmp/sysinfo.$$.bdf_warn 
+  print ""
+
+}        # end of f_filesystem_check
+
+#===================================================================
+# f_disk_capacity
+#        This function displays total disk, group, and lvol capacities.
+#        The data comes from the logical, physical, & volume functions.
+#        Therefore, this function does not stand alone. It must be run
+#        with the l, p, & v options.
+#===================================================================
+function f_disk_capacity
+{
+  Debug "Beginning DISK CAPACITY calculations."
+  if (($HTML))
+  then
+    print "
" >> /tmp/sysinfo.$$.capacity + print "" >> /tmp/sysinfo.$$.capacity + print "

" >> /tmp/sysinfo.$$.capacity + print "

TOP

" >>/tmp/sysinfo.$$.capacity + print "" >> /tmp/sysinfo.$$.capacity + print "Disk Capacity Data

" >> /tmp/sysinfo.$$.capacity + print "
" >> /tmp/sysinfo.$$.capacity
+  else
+    print "DISK CAPACITY DATA" >> /tmp/sysinfo.$$.capacity
+    print "==================" >> /tmp/sysinfo.$$.capacity
+  fi
+  ((unused_cap = total_p_mb - TotalAllocMb))
+  ((OtherMb = TotalAllocMb - total_l_mb - TotalMirrorMb))
+  print "Logical Volume Space  =    $total_l_mb MB" >> /tmp/sysinfo.$$.capacity
+  print "Mirror Space          =    $TotalMirrorMb MB" >> /tmp/sysinfo.$$.capacity
+  print "Other Space Allocated =    $OtherMb MB" >> /tmp/sysinfo.$$.capacity
+  print "                        -----------" >> /tmp/sysinfo.$$.capacity
+  print "Total Disk Allocated  =    $TotalAllocMb MB" >> /tmp/sysinfo.$$.capacity
+  print "Unallocated Disk      =    $unused_cap MB" >> /tmp/sysinfo.$$.capacity
+  print "                        ===========" >> /tmp/sysinfo.$$.capacity
+  print "Total Physical Disk   = $total_p_mb MB\n" >> /tmp/sysinfo.$$.capacity
+
+  print "Checked:" >> /tmp/sysinfo.$$.capacity
+  print "${pcount} physical volumes" >> /tmp/sysinfo.$$.capacity
+  print "${vgcount} volume groups" >> /tmp/sysinfo.$$.capacity
+  print "${lcount} logical volumes" >> /tmp/sysinfo.$$.capacity
+
+  Debug "  found ${pcount} physical volumes"
+  Debug "  found ${vgcount} volume groups"
+  Debug "  found ${lcount} logical volumes"
+  
+  if  ((${stale}))
+  then
+    Debug "found ${total_stale} STALE extents"
+    Debug "  calculating re-syncing time"
+    exit_code=${SYS_ERROR}
+    print "STALE extents detected!" >> /tmp/sysinfo.$$.capacity
+    print "  check error listing for more details." >> /tmp/sysinfo.$$.capacity
+    print -n "\n\nERROR:${sysname}: ${total_stale}" >> /tmp/sysinfo.$$.errwarn
+    print    " stale extents detected.\n" >> /tmp/sysinfo.$$.errwarn
+    print -n "Estimated time to re-sync ${total_stale}" >> /tmp/sysinfo.$$.errwarn
+    print    " stale extents is" >> /tmp/sysinfo.$$.errwarn
+    if ((${TotalStaleMb} < 100))
+    then
+      print " less than 1 minute." >> /tmp/sysinfo.$$.errwarn
+      Debug " less than 1 minute" 
+    else
+      ((TIME = TotalStaleMb / 100))
+      ((HOURS = TIME /60))
+      ((MINUTES = TIME % 60))
+      print " ${HOURS} hours and ${MINUTES} minutes." >> /tmp/sysinfo.$$.errwarn
+      Debug " ${TIME} total minutes or" 
+      Debug "     ${HOURS} hours and ${MINUTES} minutes" 
+    fi
+    print >> /tmp/sysinfo.$$.errwarn
+  else
+    print "with no stale extents detected.\n"   >> /tmp/sysinfo.$$.capacity
+    Debug "No stale extents detected"
+  fi
+  f_display_file /tmp/sysinfo.$$.capacity
+
+}                 # end of f_disk_capacity
+
+
+#===================================================================
+# f_check_root
+#        This function checks to see if the user is root.
+#===================================================================
+function f_check_root
+{
+  id | grep '(root)' > /dev/null
+  if (($?))
+  then
+    print "\nYou must be super-user to run ${script}.\n"
+    exit ${ERROR}
+  fi
+}                # end of check_root
+
+#===================================================================
+# f_check_hpux
+#        This function checks to see if running on HP-UX
+#===================================================================
+function f_check_hpux
+{
+  uname -s | grep HP-UX > /dev/null
+  if (($?))
+  then
+    print "\nThis utility is only supported on HP-UX systems!\n"
+    exit ${ERROR}
+  fi
+}                # end of check_hpux
+
+#===================================================================
+# f_extract_errors
+#        This function extract WARNINGS & ERRORS from the logfile
+#===================================================================
+function f_extract_errors
+{
+  if [[ -f /tmp/sysinfo.$$.errwarn ]]
+  then
+    if (($HTML))
+    then
+      print "
" >> /tmp/sysinfo.$$.errwarn.header + print "" >> /tmp/sysinfo.$$.errwarn.header + print "

" >> /tmp/sysinfo.$$.errwarn.header + print "

" >> /tmp/sysinfo.$$.errwarn.header + print "TOP

" >> /tmp/sysinfo.$$.errwarn.header + print "" >> /tmp/sysinfo.$$.errwarn.header + print "Summary of Errors/Warnings

" >> /tmp/sysinfo.$$.errwarn.header + print "
"   >> /tmp/sysinfo.$$.errwarn.header
+    else
+      print ""                                >> /tmp/sysinfo.$$.errwarn.header
+      print "ERROR/WARNING SUMMARY"           >> /tmp/sysinfo.$$.errwarn.header
+      print "====================="           >> /tmp/sysinfo.$$.errwarn.header
+    fi
+
+    grep  -e WARNING -e ERROR /tmp/sysinfo.$$.errwarn > /dev/null 2>&1
+    if [ $? -eq 1 ]
+    then
+      Debug "No errors/warnings found."
+      if (($BATCH))
+      then
+        print "No ERRORS or WARNINGS found."
+      fi
+      print "($sysname): No errors/warnings found." >> /tmp/sysinfo.$$.errwarn
+    else
+      Debug "Found errors/warnings, displaying /tmp/sysinfo.$$.errwarn"
+      if (($BATCH))
+      then
+        print "Errors and/or Warnings were found." 
+        print "Please check output file ${LOGFILE}."
+      fi
+    fi
+    print "" >> /tmp/sysinfo.$$.errwarn
+    f_display_file /tmp/sysinfo.$$.errwarn.header
+    f_display_file /tmp/sysinfo.$$.errwarn
+  else
+    print "Could not find /tmp/sysinfo.$$.errwarn"
+  fi
+}
+
+function print_header
+{
+#print
+#print " ****             *****"
+#print "*                   *                       "
+#print "*     *   *  ***    *   **   * ****  **** "
+#print " ****  * *  *       *   * *  * *    *    *"
+#print "     *  *    ***    *   *  * * ***  *    *"
+#print "     *  *       *   *   *   ** *    *    *"
+#print " ****   *    ***  ***** *    * *     ****"
+#print
+print
+print " @@@@             @@@@@"
+print "@                   @                       "
+print "@     @   @  @@@    @   @@   @ @@@@  @@@@ "
+print " @@@@  @ @  @       @   @ @  @ @    @    @"
+print "     @  @    @@@    @   @  @ @ @@@  @    @"
+print "     @  @       @   @   @   @@ @    @    @"
+print " @@@@   @    @@@  @@@@@ @    @ @     @@@@"
+print
+}
+#===================================================================
+# BEGIN MAIN CODE
+#===================================================================
+typeset -fx lvm_scan
+typeset -fx sam_scan
+typeset -fx io_scan
+typeset -fx f_query_sam
+sysname=$(hostname)
+where=`dirname ${0}`
+f_check_hpux
+f_check_root
+get_args $*
+if (($BATCH))
+then
+  #print "\n${script}  ${version} by Scott Truesdale\n"
+  print_header
+  print "Author:  Scott Truesdale"
+  print "Version: ${version}\n"
+  if (($HTML))
+  then
+    print ""                                 >> $LOGFILE
+    print ""                                 >> $LOGFILE_INDEX
+    print ""                                 >> $LOGFILE_MAIN
+    print "Sysinfo for ${sysname}"        >> $LOGFILE
+    print "Sysinfo (Index) for ${sysname}" >> $LOGFILE_INDEX
+    print "Sysinfo (Main) for ${sysname}" >> $LOGFILE_MAIN
+    print ""                                      >> $LOGFILE
+    print "> $LOGFILE
+    print "BORDERCOLOR='darkblue' FRAMESPACING='2'"      >> $LOGFILE
+    print "BORDER='2'>"                                  >> $LOGFILE
+    print "> $LOGFILE
+    print "NAME='IndexFrame' SCROLLING='NO'>"            >> $LOGFILE
+    print "> $LOGFILE
+    print "NAME='MainFrame'"                             >> $LOGFILE
+    print "SCROLLING='AUTO'>"                  >> $LOGFILE
+    print "<BODY BGCOLOR=lightblue>"                     >> $LOGFILE
+    print "</HEAD><BODY BGCOLOR=lightblue>"              >> $LOGFILE_INDEX
+    print "</HEAD><BODY BGCOLOR=lightblue>"              >> $LOGFILE_MAIN
+    print "<CENTER>"                                     >> $LOGFILE
+    print "<A name='TOP'></A>"                           >> $LOGFILE
+    print "<FONT SIZE=6 COLOR=darkblue>"                 >> $LOGFILE
+    print "${script} ${version} "                        >> $LOGFILE
+    print "by Scott Truesdale</FONT><BR>"                >> $LOGFILE
+    print "<FONT SIZE=6>Configuration Data for"          >> $LOGFILE
+    print "<FONT COLOR=red>${sysname}</FONT></FONT><BR>" >> $LOGFILE
+    print "<FONT SIZE=3>Collected on `date`.</FONT><BR>" >> $LOGFILE
+    print "<A HREF='#System'>System</A>"                 >> $LOGFILE
+    print "<A HREF='#Kernel'>Kernel</A>"                 >> $LOGFILE
+    print "<A HREF='#Network'>Network</A>"               >> $LOGFILE
+    print "<A HREF='#File'>File System</A>"              >> $LOGFILE
+    print "<BR>"                                         >> $LOGFILE
+    print "<A HREF='#IO'>IO Configuration</A>"           >> $LOGFILE
+    print "<A HREF='#Physical'>Physical Disks</A>"       >> $LOGFILE
+    print "<A HREF='#Pmap'>Physical Mapping</A>"         >> $LOGFILE
+    print "<A HREF='#Volume'>Volume Groups</A>"          >> $LOGFILE
+    print "<BR>"                                         >> $LOGFILE
+    print "<A HREF='#Logical'>Logical Disks</A>"         >> $LOGFILE
+    print "<A HREF='#Lmap'>Logical Mapping</A>"          >> $LOGFILE
+    print "<A HREF='#Capacity'>Disk Capacity</A>"        >> $LOGFILE
+    print "<A HREF='#Software'>Software List</A>"        >> $LOGFILE
+    print "<BR>"                                         >> $LOGFILE
+    print "<A HREF='#SYSACCESS'>Security Check</A>"      >> $LOGFILE
+    print "<A HREF='#DIAGNOSTICS'>Diagnostic Settings</A>" >> $LOGFILE
+    print "<A HREF='#LOGFILES'>System Logfiles</A>"      >> $LOGFILE
+    print "<A HREF='#ErrWarn'>Errors/Warnings</A>"       >> $LOGFILE
+    if (($PASS))
+    then
+      print "<BR>"                                       >> $LOGFILE
+      print "<A HREF='#SAP'>SAP R/3 Info</A>"            >> $LOGFILE
+    fi
+    print "</CENTER>"                                    >> $LOGFILE
+    print "<CENTER>"                                     >> $LOGFILE_INDEX
+    print "<A name='TOP'></A>"                           >> $LOGFILE_INDEX
+    print "<FONT SIZE=6 COLOR=darkblue>"                 >> $LOGFILE_INDEX
+    print "${script} ${version} "                        >> $LOGFILE_INDEX
+    print "by Scott Truesdale</FONT><BR>"                >> $LOGFILE_INDEX
+    print "<FONT SIZE=6>Configuration Data for"          >> $LOGFILE_INDEX
+    print "<FONT COLOR=red>${sysname}</FONT></FONT><BR>" >> $LOGFILE_INDEX
+    print "<FONT SIZE=3>Collected on `date`.</FONT><BR>" >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#System' TARGET='MainFrame'>System</A>"                 >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#Kernel' TARGET='MainFrame'>Kernel</A>"                 >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#Network' TARGET='MainFrame'>Network</A>"               >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#File' TARGET='MainFrame'>File System</A>"              >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#IO' TARGET='MainFrame'>IO Configuration</A>"           >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#Physical' TARGET='MainFrame'>Physical Disks</A>"       >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#Pmap' TARGET='MainFrame'>Physical Mapping</A>"         >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#Volume' TARGET='MainFrame'>Volume Groups</A>"          >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#Logical' TARGET='MainFrame'>Logical Disks</A>"         >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#Lmap' TARGET='MainFrame'>Logical Mapping</A>"          >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#Capacity' TARGET='MainFrame'>Disk Capacity</A>"        >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#Software' TARGET='MainFrame'>Software List</A>"        >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#SYSACCESS' TARGET='MainFrame'>Security Check</A>"      >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#DIAGNOSTICS' TARGET='MainFrame'>Diagnostic Settings</A>" >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#LOGFILES' TARGET='MainFrame'>System Logfiles</A>"      >> $LOGFILE_INDEX
+    print "<A HREF='${LOGFILE_MAIN_REL}#ErrWarn' TARGET='MainFrame'>Errors/Warnings</A>"       >> $LOGFILE_INDEX
+    if (($PASS))
+    then
+      print "<A HREF='${LOGFILE_MAIN_REL}#SAP' TARGET='MainFrame'>SAP R/3 Info</A>"            >> $LOGFILE_INDEX
+    fi
+    print "</CENTER>"                                    >> $LOGFILE_INDEX
+
+  else
+    print "\n${script} ${version} by Scott Truesdale\n" >> $LOGFILE
+    print "Configuration data for ${sysname}\ncollected on `date`\n" >> $LOGFILE
+  fi
+else
+  print_header
+  print "Author:  Scott Truesdale"
+  print "Version: ${version}\n"
+  print "Configuration data for ${sysname}\ncollected on `date`.\n" 
+fi
+touch /tmp/sysinfo.$$.errwarn
+#===================================================================
+# Begin PASS section
+#===================================================================
+if (($PASS))
+then
+  touch /tmp/sysinfo.$$.sapwarn
+  check_for_sap
+fi
+
+#===================================================================
+#  SYSTEM CHECK AND ROOT DATA 
+#===================================================================
+f_get_sys_type
+
+if (($SYSTEM))
+then
+  f_get_system_data
+fi
+#===================================================================
+# KERNEL DATA 
+#===================================================================
+if (($FULL_KERNEL)) || (($LITE_KERNEL))
+then
+  if (( ${osmajor} >= 10 ))
+  then
+    f_get_kernel_data
+  else
+    f_get_9x_kernel_data
+  fi
+fi
+
+#if (($SAMSCAN)) && (( ${osmajor} >= 10 ))
+#then
+#  f_display_sam_kernel_data
+#fi
+
+#===================================================================
+# NETWORK CARD DATA
+#===================================================================
+if (($NETWORK))
+then
+  f_get_network_data
+fi
+
+#===================================================================
+# FILE SYSTEM CHECK
+#===================================================================
+if (($FILESYSTEM))
+then
+  f_filesystem_check
+fi
+
+#===================================================================
+# IOSCAN CHECK
+#===================================================================
+if (($IOSCAN))
+then
+  f_get_ioscan_data
+fi
+
+#===================================================================
+# PHYSICAL DISK CHECK
+#===================================================================
+if (($PHYSICAL))
+then
+  f_get_physical_disk_data
+fi
+
+#===================================================================
+# VOLUME GROUP CHECK
+#===================================================================
+if (($VOLUMES)) 
+then
+  if (($lvm_installed))
+  then
+    f_get_volume_group_data
+  else
+    print "LVM must be installed to use this option."
+  fi
+fi
+
+#===================================================================
+# LOGICAL DISK CHECK
+#===================================================================
+if (($LOGICAL)) 
+then
+  if (($lvm_installed))
+  then
+    f_get_logical_volume_data
+  else
+    print "LVM must be installed to use this option."
+  fi
+fi
+
+#===================================================================
+# LOGICAL VOLUME TO PHYSICAL DISK MAPPING 
+#===================================================================
+# check which physical disks have this logvol on them
+if (($LMAP))
+then
+  if (($lvm_installed))
+  then
+    f_logical_to_physical
+  else
+    print "LVM must be installed to use this option."
+  fi
+fi
+
+#===================================================================
+# DISK CAPACITY TABLE
+#===================================================================
+# note that the CAPACITY flag cannot be set directly.
+# it is automatically enabled when either the -a or -lpv flags
+if (($CAPACITY))
+then
+  if (($lvm_installed))
+  then
+    f_disk_capacity
+  else
+    print "LVM must be installed to use this option."
+  fi
+fi
+
+#===================================================================
+# SOFTWARE LIST
+#===================================================================
+if (($SWLIST))
+then
+  sw_scan
+fi
+
+#===================================================================
+# Check system file/directory access and some security parms.
+#===================================================================
+if (($FILEACCESS))
+then
+  chk_sysaccess
+fi
+
+#===================================================================
+# Check to see if the system has diagnostics
+#===================================================================
+if (($DIAGNOSTICS))
+then
+  chk_diags
+  chk_ignite
+fi
+
+#===================================================================
+# Check System Logfiles
+#===================================================================
+
+if (($LOGFILES))
+then
+  chk_logfiles
+fi
+
+#===================================================================
+# Extract ERROR/WARNING messages
+#===================================================================
+f_extract_errors
+
+if (($PASS))
+then
+  f_display_file /tmp/sysinfo.$$.sap
+  print "\n\n" >> /tmp/sysinfo.$$.sapwarn
+  f_display_file /tmp/sysinfo.$$.sapwarn
+
+fi
+
+#===================================================================
+# Clean up end of HTML files if necessary
+#===================================================================
+
+if (($HTML))
+then
+  print "</BODY></HTML>" >> $LOGFILE_INDEX
+  print "</BODY></HTML>" >> $LOGFILE_MAIN
+  print "</BODY>" >> $LOGFILE
+  chmod 444 $LOGFILE_INDEX $LOGFILE_MAIN $LOGFILE
+fi
+
+#===================================================================
+# END OF SCRIPT - SO LET'S GET OUT OF HERE!
+#===================================================================
+Debug "exit_code = ${exit_code}" 
+exit ${exit_code}
+
diff --git a/clients/HP/bin/sysmodel b/clients/HP/bin/sysmodel
new file mode 100644
index 0000000..6def4b7
--- /dev/null
+++ b/clients/HP/bin/sysmodel
@@ -0,0 +1,230 @@
+#!/bin/ksh
+# Oslo Feb. 2 1995
+# sys_info 1.3, a hack by Ole Kristian Foss, HPNAS,
+# to display HP9000 model and the clock frequency of the CPU(s)
+# Version 1.0                                                 Oslo Jan. 25
+1995
+# Version 1.1 added call to the $SCHED_FILE.                  Oslo Jan. 25
+1995
+# Version 1.2 added the get_physmem function.                 Oslo Feb. 2
+1995
+# Version 1.3 added limited functionality for HP-UX 10.X.     Oslo Feb. 28
+1995
+# Version 1.4 more robust 10.X handling.                      Oslo Mar. 13
+1996
+#             This version should handle all cominations of
+#             HP-UX 9.X and 10.X as well as S700 and S800
+#
+#
+# Initialize variables:
+ADB="adb -k"
+ADB_CMD=""
+MODEL=""
+PA_INFO=""
+PA_RISC=""
+CPU_REV=""
+OS_REV=""
+VER="1.4"
+CORES=""
+PROG=`basename $0`
+SCHED_FILE="/usr/lib/sched.models"
+export PATH=/etc:/bin:/usr/bin
+typeset -R4 ID=""
+typeset -i FADB_OUT
+typeset -i MHZ
+typeset -i HZ
+typeset -i SPEED
+#
+function usage
+{
+        print -u2 "Usage: $PROG <-h|-m> [-v]"
+        exit 1
+}
+#
+function get_os_rev
+{
+        OS_REV=`uname -r| cut -f2 -d"."`
+        case $OS_REV in
+                09) CORES="/hp-ux /dev/kmem"
+                   if /bin/hp9000s700
+                   then
+                        ADB_CMD="mpproc_info+34/X"
+                   elif /bin/hp9000s800
+                   then
+                         ADB_CMD="mpproc_info+1E8/X"
+                   else
+                        echo "This machine is not supported by $PROG"
+                   exit 1
+                   fi ;;
+
+                10) CORES="/stand/vmunix /dev/kmem"
+                    ADB_CMD="_mpproc_info+270/X" ;;
+
+                 *) echo "$PROG does not support this version of HP-UX"
+                    exit 1 ;;
+        esac
+
+}
+function get_id
+{
+        ID=`echo $ADB_CMD | $ADB $CORES |  tail -1 | cut -d: -f 2`
+}
+#
+#
+function get_model
+{
+        case $ID in
+                "  40"|0040) MODEL="840" ;;
+                "  80"|0080) MODEL="825" ;;
+                "  A0"|00A0) MODEL="835/635" ;;
+                "  B0"|00B0) MODEL="845/645" ;;
+                "  C0"|00C0) MODEL="850" ;;
+                " 810"|0810) MODEL="855" ;;
+                " 820"|0820) MODEL="860" ;;
+                " 830"|0830) MODEL="865/870" ;;
+                1010) MODEL="822" ;;
+                1020) MODEL="832" ;;
+                1040) MODEL="842" ;;
+                1050) MODEL="852" ;;
+                1810) MODEL="890" ;;
+                1820) MODEL="891/T500" ;;
+                1830) MODEL="892/T520" ;;
+                2000) MODEL="720" ;;
+                2010) MODEL="750" ;;
+                2020) MODEL="730" ;;
+                2030) MODEL="735" ;;
+                2040) MODEL="755" ;;
+                2060) MODEL="735/125" ;;
+                2800) MODEL="817/F20" ;;
+                2800) MODEL="827/H20" ;;
+                2810) MODEL="837/F30" ;;
+                2810) MODEL="847/G30/H30" ;;
+                2810) MODEL="857/I30" ;;
+                2820) MODEL="807/F10" ;;
+                2830) MODEL="867/G40/H40" ;;
+                2830) MODEL="877/I40" ;;
+                2840) MODEL="887/G50/H50" ;;
+                2840) MODEL="897/I50" ;;
+                2870) MODEL="887/G70/H70" ;;
+                2870) MODEL="897/I70" ;;
+                2880) MODEL="887/G60/H60" ;;
+                2880) MODEL="897/I60" ;;
+                3000) MODEL="710" ;;
+                3020) MODEL="705" ;;
+                3100) MODEL="715/50" ;;
+                3110) MODEL="715/33" ;;
+                3120) MODEL="715s/50" ;;
+                3130) MODEL="715s/33" ;;
+                3140) MODEL="715t/50" ;;
+                3150) MODEL="715t/33" ;;
+                3160) MODEL="715/75" ;;
+                3180) MODEL="725/50" ;;
+                3190) MODEL="725/75" ;;
+                4010) MODEL="745i/50" ;;
+                4020) MODEL="742i" ;;
+                4030) MODEL="745i/100" ;;
+                4800) MODEL="806/E25" ;;
+                4810) MODEL="816/E35" ;;
+                4820) MODEL="826/E45" ;;
+                4830|4831) MODEL="856/E55" ;;
+                5800) MODEL="809/K100" ;;
+                5810) MODEL="839/K210" ;;
+                5820) MODEL="829/K400" ;;
+                5830) MODEL="849/K410" ;;
+                5870) MODEL="841/D210/D410" ;;
+                5880) MODEL="851/D210/D4102-way" ;;
+                5890) MODEL="821/D200/D400" ;;
+                58A0) MODEL="831/D200/D4002-way" ;;
+                58B0) MODEL="819/K200" ;;
+                58C0) MODEL="859/K230" ;;
+                58D0) MODEL="869/K430" ;;
+                6000) MODEL="712/60" ;;
+                6010) MODEL="712/80" ;;
+                6020) MODEL="712/100" ;;
+                6030) MODEL="743i/60" ;;
+                6040) MODEL="743i/100" ;;
+                60A0) MODEL="715/64" ;;
+                60B0) MODEL="715/100" ;;
+                60D0) MODEL="725/100" ;;
+                6170) MODEL="V743i" ;;
+                6180) MODEL="V743i/100" ;;
+                6190) MODEL="715/80" ;;
+                61A0) MODEL="811/DX5" ;;
+                61B0) MODEL="801/DXO" ;;
+                *)     MODEL="UNKNOWN" ;;
+        esac
+}
+#
+function get_tics
+{
+        ADB_CMD="itick_per_tick/D"
+        FADB_OUT=`echo $ADB_CMD | $ADB $CORES | tail -1 | cut -f 2`
+#
+        if [ $1 = "HZ" ]
+        then
+                ((HZ=$FADB_OUT * 100))
+                SPEED=$HZ
+        else
+                ((MHZ=$FADB_OUT / 10000))
+                SPEED=$MHZ
+        fi
+}
+#
+function get_physmem
+{
+        ADB_CMD="physmem/D"
+        PHYS_MEM=`echo $ADB_CMD | $ADB $CORES | tail -1 | \
+        awk '$2 > 0 { print $2 / 256 }' `
+        echo "Your system has $PHYS_MEM Mbyte ram installed"
+}
+#
+function get_painfo
+{
+        MODEL="`echo $MODEL|cut -f1 -d'/'`"
+        PA_INFO=`grep ^$MODEL $SCHED_FILE`
+        if [  -n "$PA_INFO" ]
+        then
+                PA_RISC="`echo $PA_INFO | cut -d' ' -f 3 `"
+                CPU_REV="`echo $PA_INFO | cut -d' ' -f 2 `"
+                echo "The PA-RISC processor(s) is of type $PA_RISC rev.
+$CPU_REV"
+        fi
+}
+
+## Main program ##
+if [ $# -eq 0 ]
+then
+        usage
+        exit 1
+fi
+#
+if [ `whoami` != "root" ]
+then
+        echo "You must be root to run $PROG ! "
+        exit 1
+fi
+
+while getopts :mMhHv OPT
+do case $OPT in
+   h|H) get_os_rev
+        get_id
+        get_model
+        get_tics HZ
+        print "Your HP9000 Model $MODEL has CPU(s) running at $SPEED Hz"
+        get_painfo
+        get_physmem
+        ;;
+   m|M) get_os_rev
+        get_id
+        get_model
+        get_tics MHZ
+        print "Your HP9000 Model $MODEL has CPU(s) running at $SPEED MHz"
+        get_painfo
+        get_physmem
+        ;;
+     v) echo "You are running version $VER of $PROG"
+        ;;
+   \?)  print -u2 "$PROG: unknown option $OPTARG"
+        usage
+   esac
+done
diff --git a/clients/HP/bin/tbdf b/clients/HP/bin/tbdf
new file mode 100644
index 0000000..ad7d5a6
--- /dev/null
+++ b/clients/HP/bin/tbdf
@@ -0,0 +1,76 @@
+#! /bin/sh
+
+# tbdf (total bdf 'cause it shows the total K bytes used)
+#
+# written by Tom Bukowski 1990,1991
+#  tbdf version 2.2   Jan 4, 1993
+# @(#)  tbdf version 3.0   Aug 19, 1996
+
+egrepCmd=''
+
+if [ $# -eq 0 ];then
+        command=`mount|cut -d' ' -f1-4|grep -v ':'| cut -d' ' -f1`
+
+elif [ $1 = 'help' -o $1 = 'HELP' ];then
+
+        echo "Usage: $0 [-r][-rv] [pattern]"
+        echo '\n  options:'
+        echo '    -r   total all filesystems beginning with
+pattern'
+        echo '    -rv  total all filesystems excluding pattern\n'
+
+        exit
+
+elif [ $1 != "-r" -a $1 != "-rv" ];then
+        command=$*
+
+elif [ $1 = "-r" -o $1 = "-rv" ];then
+
+        test $1 = '-rv' && opt='-v'
+        shift
+        egrepCmd=$1
+        if [ $# -gt 1 ];then
+                while [ $# -gt 0 ]
+                do
+                        egrepCmd="$egrepCmd|^$1"
+                        shift
+                done
+        fi
+
+        command=`mount |\
+        awk '{printf"%s %s %s\n",$1,$2,$3 }' |\
+        grep -v : |\
+        cut -d' ' -f1 |\
+        egrep $opt $egrepCmd |\
+        awk '{ print $NF }'`
+        test -z "$command" && exit
+
+fi
+
+if [ -x /usr/bin/nawk ];then
+ AWK=/usr/bin/nawk
+else
+ AWK=awk
+fi
+
+bdf $command |\
+$AWK '
+{
+        print
+        if ( NR > 1 )
+           if ( NF == 6) {
+                Kbytes_total += $2
+                Used_total += $3
+                Avail_total += $4
+           } else
+            if ( NF == 5 ) {
+                Kbytes_total += $1
+                Used_total += $2
+                Avail_total += $3
+            }
+
+}
+        END { printf"%43s\n","------  ------  ------"
+              printf"%27d %7d %7d\n",Kbytes_total,Used_total,Avail_total
+            }'
+
diff --git a/clients/HP/bin/testmach b/clients/HP/bin/testmach
new file mode 100644
index 0000000..88d86d4
--- /dev/null
+++ b/clients/HP/bin/testmach
@@ -0,0 +1,59 @@
+#!/bin/ksh
+################################################################################
+#
+# File:         testmach
+# Description:  A script to execute a command on all test class machines
+# Author:       Andrew@DeFaria.com
+# Created:      Thu May 11 11:08:24 PDT 2000
+# Modified:
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+# Set me to command name
+me=$(basename $0)
+
+# Set adm_base
+adm_base=${adm_base:-$HOME/adm}
+
+# Set adm_fpath
+adm_fpath=${adm_fpath:-$adm_base/functions}
+
+# Source functions
+. $adm_fpath/common
+
+# Set machines
+machines=${machines:-$adm_base/data/machines}
+
+if [ "$1" = "-f" ]; then
+  shift
+  machines="$1"
+  shift
+fi
+
+PATH=/adm/bin:$PATH
+
+if [ "$1" = "-r" ]; then
+  root=yes
+  shift
+fi
+
+for test_machine in $(grep -ve ^# $machines | grep :Test: | cut -d: -f1); do
+  # Execute command. Note if no command is given then the effect is to
+  # rlogin to each machine.
+  print "$test_machine:$@"
+  if [ $# -gt 0 ]; then
+    if [ -z "$root" ]; then
+      remsh $test_machine -n "$@"
+    else
+      root remsh $test_machine -n "$@"
+    fi
+  else
+    if [ -z "$root" ]; then
+      remsh $test_machine
+    else
+      root remsh $test_machine
+    fi
+  fi
+done
diff --git a/clients/HP/bin/trim_sd_logs b/clients/HP/bin/trim_sd_logs
new file mode 100644
index 0000000..f965a7a
--- /dev/null
+++ b/clients/HP/bin/trim_sd_logs
@@ -0,0 +1,124 @@
+#!/bin/ksh
+################################################################################
+#
+# File:         trim_sw_logs
+# Description:  Script to trim logfiles produced by SD-UX
+# Author:       Andrew@DeFaria.com (Derived from /usr/sbin/cleanup on HP-UX
+#               10.20)
+# Created:      Thu Feb 17 16:12:17 PST 2000
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+# Set me to command name
+me=$(basename $0)
+
+# Set adm_base
+adm_base=${adm_base:-$HOME/adm}
+
+# Set adm_fpath
+adm_fpath=${adm_fpath:-$adm_base/functions}
+
+# Source functions
+. $adm_fpath/common
+tmpprefix=/tmp/$me
+. $adm_fpath/tmpfiles
+trap cleanup INT EXIT ERR
+
+function usage {
+  if [ "_$1" != "_" ]; then
+    display "$1"
+    display
+  fi
+  display "Usage: $me"
+  exit 1
+} # usage
+
+# Check for execution by root
+if is_not_root; then
+  error "This script must be run as root" 1
+fi
+
+while [ $# -ge 1 ]; do
+  case "$1" in
+    -usage)
+      usage
+    ;;
+
+    -v|-verbose)
+      verbose=yes
+    ;;
+
+    -d|-debug)
+      debug=yes
+    ;;
+
+    *)
+      usage "Unrecognized parameter $1"
+    ;;
+  esac
+  shift
+done
+
+logdir=/var/adm/sw
+
+# This routine will trim the given SD logfile by deleting old session
+# log information.  By specifying a date in the mm/dd/yy format, the
+# user can trim all log information for sessions prior to the date.
+function trim_sd_logfile {
+  date=$1
+  logfile=$2
+
+  # Process logfile.
+  # Toss all lines until a line of the form:    =======  date
+  # is discovered.  Retain all lines after recognizing this one.
+  awk '
+    print_all == 1 \
+      {
+      print;
+      next;
+      }
+
+      /^=======  / \
+      {
+      key = sprintf("%s%s", "=======  ", searchdate);
+      if ( index($0, key) == 1 )
+        {
+        print;
+        print_all = 1;
+        }
+      next;
+      }
+' searchdate="$date" $logfile > $tmpprefix
+
+  # Check the size of the tmp file to see if any trimming occurred
+  # If the tmp file is zero-length, then do NOT overwrite the logfile.
+
+  length=$(wc -l $tmpprefix | awk '{print $1}')
+
+  if [ "$length" != "0" ]; then
+    cat $tmpprefix 2>/dev/null > $logfile
+    if [ $? != 0 ]; then
+       warning "Cannot overwrite $logfile" 1
+    fi
+  fi
+} # trim_sd_logfile
+
+function trim_sd_logfiles {
+  cd $logdir
+  for file in $(ls sw*.log); do
+    if [ -s $file ]; then
+      verbose "Trimming $file"
+
+      # Get a suitable date from the file to pass to "trim" The date will be
+      # the 2nd to last date mentioned in  the logfile.
+      target_date=$(awk '/^=======/ {print $2, $3}' $file | \
+                                   uniq | tail -5 | head -1)
+
+      trim_sd_logfile "$target_date" $file
+    fi
+  done
+} # trim_sd_logfiless
+
+trim_sd_logfiles
diff --git a/clients/HP/bin/unmount_nfs b/clients/HP/bin/unmount_nfs
new file mode 100644
index 0000000..333a2f7
--- /dev/null
+++ b/clients/HP/bin/unmount_nfs
@@ -0,0 +1,93 @@
+#!/bin/ksh
+################################################################################
+#
+# File:         mount_nfs
+# RCS:          $Header:$
+# Description:  A script to mount all nfs mounts. Note if the automounter is
+#		running then this script will first shutdown the automounter.
+#		This script returns 0 for success or non zero if it was unable
+#		to umount all nfs mounts. This script must run as root.
+# Author:       Andrew DeFaria, California Language Labs
+# Created:      Fri Jun  6 10:31:51 PDT 1997
+# Modified:     
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+# Set me to command name
+me=$(basename $0)
+
+# Set adm_base
+adm_base=${adm_base:-$HOME/adm}
+
+# Set adm_fpath
+adm_fpath=${adm_fpath:-$adm_base/functions}
+
+# Source functions
+. $adm_fpath/common
+
+if is_not_root; then
+  error "This script must be run as root" 1
+fi
+
+integer automount_pid=$(process_is_running automount)
+kill_automounter=yes
+
+if [ $automount_pid -ne 0 ]; then
+  print "Attempting to shutdown the automounter..."
+  kill -15 $automount_pid
+
+  print "Waiting for the automounter to shutdown..."
+  integer max_tries=5
+  integer wait_time=10
+
+  while [ $max_tries -ne 0 ]; do
+    sleep 10
+    automount_pid=$(process_is_running automount)
+    process_is_running automount
+    if [ $automount_pid -ne 0 ]; then
+      print "The automounter ($automount_pid) is still running!"
+      print "I will wait $max_tries more time\c"
+      if [ $max_tries -gt 1 ]; then
+	print -u2 "s\c"
+      fi
+      print ". Waiting $wait_time seconds..."
+      sleep $wait_time
+    else
+      break
+    fi
+    let max_tries=max_tries-1
+  done
+fi
+
+automount_pid=$(process_is_running automount)
+if [ $automount_pid -ne 0 ]; then
+  print "The automounter has not shutdown! Continuing..."
+else
+  print "The automounter has been shut down successfully"
+  touch /etc/automounter_was_here
+fi
+
+print "\nAttempting to unmount all nfs mounts"
+if [ "$OS" = "09" ]; then
+  /etc/umount -at nfs
+else
+  /usr/sbin/umount -a -F nfs
+fi
+
+integer nfs_mounts_left=$(grep -c "nfs" /etc/mnttab)
+
+if [ $nfs_mounts_left -eq 0 ]; then
+  print "All nfs filesystems have been successfully unmounted!"
+  exit 0
+else
+  print "There \c"
+  if [ $nfs_mounts_left -eq 1 ]; then
+    print "is one filesystem left mounted:\n"
+  else
+    print "are $nfs_mounts_left filesystems left mounted:\n"
+  fi
+  grep nfs /etc/mnttab
+  exit 1
+fi
diff --git a/clients/HP/bin/update_machine_info b/clients/HP/bin/update_machine_info
new file mode 100644
index 0000000..e11b40d
--- /dev/null
+++ b/clients/HP/bin/update_machine_info
@@ -0,0 +1,110 @@
+#!/bin/ksh
+################################################################################
+#
+# File:         update_machine_info
+# Description:  Updates machine infor file (/vob/admin/machines)
+# Author:       Andrew@DeFaria.com
+# Created:      Fri Apr 30 14:13:56 PDT 1999
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+# Set me to command name
+me=$(basename $0)
+
+# Set adm_base
+adm_base=${adm_base:-$HOME/adm}
+
+# Set adm_fpath
+adm_fpath=${adm_fpath:-$adm_base/functions}
+
+# Source functions
+. $adm_fpath/common
+
+# Set machines
+machines=${machines:-$adm_base/data/machines}
+
+tmp_file=/tmp/machines.$$
+machine=$(uname -n)
+ip=$(getip $machine)
+mod=$(uname -m)
+osversion=$(uname -r)
+
+verbose=false
+
+while getopts v OPT; do
+  case $OPT in
+    v) verbose=true
+    ;;
+  esac
+done
+
+if [ -x /usr/atria/bin/cleartool ]; then
+  ccversion=$(/usr/atria/bin/cleartool -version | grep "ClearCase version" |
+\
+    cut -f3 -d' ')
+  eclipseid=`ls -ld /usr/eclipse/etc 2> /dev/null | awk -F'> ' '{print $2}'
+\
+    | awk -F/ '{print $3}'`
+  [ $? -ne 0 ] && eclipseid="No Eclipse" || eclipseid=${eclipseid#eclipse}
+else
+  ccversion="Non ClearCase Machine"
+  eclipseid="No Eclipse"
+fi
+
+owner=Unknown
+usage=Unknown
+location=Unknown
+phone=Unknown
+class=Unknown
+
+# Unix doesn't really have a way to store such information such as owner,
+# usage and location. Attempt to ascertain this info from /etc/motd.
+if [ -f /etc/motd ]; then
+  usage=$(grep "^Usage:" /etc/motd | tr -s " " | cut -f2- -d" ")
+  owner=$(grep "^Owner:" /etc/motd | tr -s " " | cut -f2- -d" ")
+  phone=$(grep "^Phone:  " /etc/motd | tr -s " " | cut -f2- -d" ")
+  class=$(grep "^Class:" /etc/motd | tr -s " " | cut -f2- -d" ")
+  location=$(grep "^Location:" /etc/motd | tr -s " " | cut -f2- -d" ")
+fi
+
+rm -f $tmp_file
+
+if [ $verbose = "true" ]; then
+  print "Machine: $machine"
+  print "IP Address: $ip"
+  print "Model: $mod"
+  print "OS Version: $osversion"
+  print "ClearCase Version: $ccversion"
+  print "Owner: $owner"
+  print "Phone: $phone"
+  print "Usage: $usage"
+  print "Class: $class"
+  print "Location: $location\n"
+  print "Eclipse ID: $eclispeid\n"
+  print "Updating machine list...\c"
+fi
+
+# Add machine if not already present
+grep "^$machine" $machines > /dev/null 2>&1
+
+if [ $? -ne 0 ]; then
+  print "$machine" >> $machines
+fi
+
+while read line; do
+  if [ "$(print $line | cut -f1 -d:)" = $machine ]; then
+    print
+"$machine:$ip:$mod:$osversion:$ccversion:$owner:$phone:$usage:$class:$location:$eclipseid"
+>> $tmp_file
+  else
+    print $line >> $tmp_file
+  fi
+done < $machines
+
+mv $tmp_file $machines
+
+if [ $verbose = "true" ]; then
+  print "done"
+fi
diff --git a/clients/HP/bin/veritas b/clients/HP/bin/veritas
new file mode 100644
index 0000000..78c34d3
--- /dev/null
+++ b/clients/HP/bin/veritas
@@ -0,0 +1,32 @@
+#!/bin/ksh
+###############################################################################
+#
+# File:         veritos
+# Description:  Run the veritos GUI
+# Author:       Andrew DeFaria 
+# Created:      Thu Jun  6 08:31:57 PDT 1996
+# Modified:     
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+###############################################################################
+me=$(basename $0)
+VRTS_veritos=/opt/VRTSvxva/bin/vxva
+SUNW_veritos=/opt/SUNWvxva/bin/vxva
+
+if [ $(/usr/xpg4/bin/id -u) -ne 0 ]; then
+  print -u2 "$me: Error: Must be root to run this command"
+  exit 1
+fi
+
+if [ -x $VRTS_veritos ]; then
+  veritos=$VRTS_veritos
+elif [ -x $SUNW_veritos ]; then
+  veritos=$SUNW_veritos
+else
+  print "$me: Error: Unable to find veritos"
+  exit 1
+fi
+
+$veritos "$@"
diff --git a/clients/HP/bin/vicron b/clients/HP/bin/vicron
new file mode 100644
index 0000000..c520074
--- /dev/null
+++ b/clients/HP/bin/vicron
@@ -0,0 +1,50 @@
+#! /bin/sh
+# upate the cron entries for the user name who is running this script
+#
+# Get info
+#
+echo Updating the cron entries for `whoami`
+TMP="/tmp/$$.`whoami`"
+TMP2="/tmp/2.$$.`whoami`"
+
+crontab -l > $TMP
+cp $TMP $TMP2
+
+vi $TMP
+
+echo "Changes made:"
+if diff $TMP2 $TMP
+then
+	exit 0
+fi
+
+echo "Install this new crontab file for `whoami` (y or n)?  \c"
+
+while true
+do
+	read response
+	case $response in
+		Y | y)	
+			crontab $TMP
+			echo "Done."
+			break
+			;;
+
+		N | n)
+			/bin/rm -f $TMP $TMP2
+			exit 0
+			;;
+		Q | q)
+			/bin/rm -f $TMP $TMP2
+			exit 0
+			;;
+
+		*)
+			echo  "	Please specify either Y or N. (Type Q to quit.)   \c"
+			;;
+	esac
+done
+
+/bin/rm -f $TMP $TMP2
+
+exit 0
diff --git a/clients/HP/bin/viewservers b/clients/HP/bin/viewservers
new file mode 100644
index 0000000..9ee196d
--- /dev/null
+++ b/clients/HP/bin/viewservers
@@ -0,0 +1,39 @@
+#!/bin/ksh
+################################################################################
+#
+# File:         viewservers
+# RCS:          $Header: viewservers,v 1.1 98/01/27 22:31:42 defaria Exp $
+# Description:  A script to execute a command on all view servers.
+# Author:       Andrew DeFaria, California Language Labs
+# Created:      Wed Mar  5 16:31:13 PST 1997
+# Modified:     Fri Jan 16 13:56:05 PST 1998
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+PATH=/adm/bin:$PATH
+
+if [ "$1" = "-r" ]; then
+  root=yes
+  shift
+fi
+
+for viewserver in $(get_info server_names viewserver); do
+  # Execute command. Note if no command is given then the effect is to
+  # rlogin to each machine.
+  print "$viewserver:$@"
+  if [ $# -gt 0 ]; then
+    if [ -z "$root" ]; then
+      remsh $viewserver -n "$@"
+    else
+      root remsh $viewserver -n "$@"
+    fi
+  else
+    if [ -z "$root" ]; then
+      remsh $viewserver
+    else
+      root remsh $viewserver
+    fi
+  fi
+done
diff --git a/clients/HP/bin/vobadm b/clients/HP/bin/vobadm
new file mode 100644
index 0000000..27c9921
--- /dev/null
+++ b/clients/HP/bin/vobadm
@@ -0,0 +1,37 @@
+#!/bin/ksh
+################################################################################
+#
+# File:         vobadm
+# Description:  A script run something as vobadm
+# Author:       Andrew@DeFaria.com
+# Created:      Mon May 17 07:35:59 PDT 1999
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+me=$(basename $0)
+
+sudo=${sudo:-/app/sudo}
+
+if [ ! -x $sudo ]; then
+  print -u2 "$me: Error: Unable to find sudo!"
+  exit 1;
+fi
+
+if [ $# -gt 0 ]; then
+  # Execute the commands
+  $sudo -u vobadm "$@"
+else
+  # Become vobadm
+  $sudo -u vobadm -s
+
+  if [ -x ~/.rc/functions ]; then
+    # Source in ksh functions (needed for set_title and set_prompt)
+    . ~/.rc/functions
+    # Reset title and prompt (if you can)
+    alias ct=/usr/atria/bin/cleartool
+    set_title
+    set_prompt
+  fi
+fi
diff --git a/clients/HP/bin/vobservers b/clients/HP/bin/vobservers
new file mode 100644
index 0000000..4372552
--- /dev/null
+++ b/clients/HP/bin/vobservers
@@ -0,0 +1,39 @@
+#!/bin/ksh
+################################################################################
+#
+# File:         vobservers
+# RCS:          $Header: vobservers,v 1.2 98/01/28 12:22:27 defaria Exp $
+# Description:  A script to execute a command on all vob servers.
+# Author:       Andrew DeFaria, California Language Labs
+# Created:      Wed Mar  5 16:31:13 PST 1997
+# Modified:
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+PATH=/adm/bin:$PATH
+
+if [ "$1" = "-r" ]; then
+  root=yes
+  shift
+fi
+
+for vobserver in $(get_info server_names vobserver); do
+  # Execute command. Note if no command is given then the effect is to
+  # rlogin to each machine.
+  print "$vobserver:$@"
+  if [ $# -gt 0 ]; then
+    if [ -z "$root" ]; then
+      remsh $vobserver -l vobadm -n "$@"
+    else
+      root remsh $vobserver -n "$@"
+    fi
+  else
+    if [ -z "$root" ]; then
+      remsh $vobserver -l vobadm
+    else
+      root remsh $vobserver
+    fi
+  fi
+done
diff --git a/clients/HP/bin/whoison b/clients/HP/bin/whoison
new file mode 100644
index 0000000..dcf2fce
--- /dev/null
+++ b/clients/HP/bin/whoison
@@ -0,0 +1,72 @@
+#!/bin/ksh
+################################################################################
+#
+# File:         whoison
+# RCS:          $Header: whoison,v 1.1 97/05/20 19:56:29 defaria Exp $
+# Description:  A script to show you who is on the system
+# Author:       Andrew DeFaria, California Language Labs
+# Created:      Wed May 14 00:40:02 PDT 1997
+# Modified:
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+tmpprefix=/tmp/whoison
+command="who | cut -f1 -d' ' | sort -u"
+
+function cleanup {
+  if [ ! -z $"tmpprefix" ]; then
+    rm -f ${tmpprefix}*
+  fi
+
+  exit
+} # cleanup
+
+trap cleanup INT EXIT ERR
+
+if [ -z "$PAGER" ]; then
+  pager=more
+else
+  pager="$PAGER"
+fi
+
+function report_whoison {
+  machine="$1"
+
+  if [ "$machine" = "$(uname -n)" ]; then
+    eval $command > $tmpprefix.$$
+  else
+    remsh $machine -n "$command" > $tmpprefix.$$
+  fi
+
+  integer nbr_of_users=$(wc -l $tmpprefix.$$ | tr -s " " | cut -f1 -d' ')
+
+  if [ $nbr_of_users -eq 1 ]; then
+    print "$nbr_of_users user on $machine:\n"
+  else
+    print "$nbr_of_users users on $machine:\n"
+  fi
+
+  integer i=1
+
+  cat $tmpprefix.$$ | while read user; do
+    print "$i: $user"
+    let i=i+1
+  done
+
+  print
+
+  rm -f $tmpprefix.$$
+} # report_whoison
+
+if [ $# -eq 0 ]; then
+  report_whoison "$(uname -n)" | $pager
+else
+  for machine in "$@"; do
+    report_whoison "$machine"
+  done | $pager
+fi
+
+who | cut -f1 -d' ' | sort -u > $tmpprefix.$$
+
diff --git a/clients/HP/bin/whosdown b/clients/HP/bin/whosdown
new file mode 100644
index 0000000..8871a4a
--- /dev/null
+++ b/clients/HP/bin/whosdown
@@ -0,0 +1,199 @@
+#!/bin/ksh
+################################################################################
+#
+# File:         whosedown
+# Description:  Pings machines listed in machines database and produces a report
+#               about which machines are down
+# Author:       Andrew@DeFaria.com
+# Created:      Thu Oct  5 09:32:21 PDT 2000
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+# Set me to command name
+me=$(basename $0)
+
+# Set adm_base
+adm_base=${adm_base:-$HOME/adm}
+
+# Set adm_fpath
+adm_fpath=${adm_fpath:-$adm_base/functions}
+
+# Source functions
+. $adm_fpath/common
+
+# Set machines
+machines=${machines:-$adm_base/data/machines}
+
+function usage {
+  if [ "_$1" != "_" ]; then
+    display "$1"
+    display
+  fi
+  display "Usage: $me [-[c|ount] n | -[u|p] [-nocolor]"
+  display
+  display "Where:"
+  display "\t-c|ount:\tNumber of pings to attempt"
+  display "\t-u|p:\t\tReport machines that are up too"
+  display "\t-nocolor\tDo not produce color output"
+  exit 1
+} # usage
+
+function print_totals {
+  # Print totals
+  display_stderr
+  display_stderr - ------------------------
+
+  if [ "$up" = "true" ]; then
+    display_stderr "Total machines:\t$total_machines"
+    display_stderr "Total up:\t$total_up"
+  fi
+  if [ $total_down -ne 0 ]; then
+    display_stderr "Total down:\t$total_down"
+  fi
+  if [ $total_weak -ne 0 ]; then
+    display_stderr "Total weak:\t$total_week"
+  fi
+
+  exit
+} # print_totals
+
+# Totals
+integer total_machines=0
+integer total_up=0
+integer total_down=0
+integer total_weak=0
+
+# Get parameters
+integer count=2
+up=false
+if [ "$interactive" = "true" ]; then
+  color_output=true
+else
+  color_output=false
+fi
+
+while [ $# -ge 1 ]; do
+  case "$1" in
+    -usage)
+      usage
+    ;;
+
+    -v|-verbose)
+      verbose=yes
+    ;;
+
+    -d|-debug)
+      debug=yes
+    ;;
+
+    -c|-count)
+      if [[ $# -lt 2 ]]; then
+        error "Count not specified" 1
+      else
+        shift
+        count=$1
+      fi
+    ;;
+
+    -u|-up)
+      up=true
+    ;;
+
+    -nocolor)
+      color_output=false
+    ;;
+
+    *)
+      usage "Unrecognized parameter $1"
+    ;;
+  esac
+  shift
+done
+
+if [ "$color_output" = "true" ]; then
+  # Define some colors
+  esc=$(print "\033")
+
+  if [ "$TERM" = "vt100" -o "$TERM" = "vt220" ]; then
+    normal="$esc[0m"
+    up_color="$esc[1m"
+    limping_color="$esc[4m"
+    down_color="$esc[5m"
+  elif [ "$TERM" = "dtterm" -o -z DTTERM ]; then
+    normal="$esc[39m"
+    up_color="$esc[32m"
+    limping_color="$esc[35m"
+    down_color="$esc[31m"
+  elif [ "$TERM" = "hp" -o "$TERM" = "hpterm" ]; then
+    normal="$esc&d@$esc&v0S"
+    down_color="$esc&v1S"
+    limping_color="$esc&v5S"
+    up_color="$esc&v2S"
+  fi
+fi
+
+# Print heading
+display_stderr "Machine\t\tState"
+display_stderr - --------\\t--------
+
+trap print_totals INT
+
+# Check each known machine
+for machine in $(grep -v ^# $machines | cut -f1 -d:); do
+  let total_machines=total_machines+1
+
+  if [ "$up" = "true" ]; then
+    # If we are displaying up machine then print the machine name first
+    display "$machine\t\c"
+    if [ ${#machine} -lt 8 ]; then
+      display "\t\c"
+    fi
+  fi
+
+  # ping the machine
+  ping_result=$(ping $machine -n $count | tail -1 | cut -c44-)
+  integer state=0
+
+  # Translate the return string to states
+  case "$ping_result" in
+    "100% packet loss") # Total packet loss - machine is downes
+      state=1
+      ;;
+
+    "") # No packet loss - machine is fine
+      state=0
+      ;;
+
+    *) # Some other percentage of packet loss - machine is limping
+      state=2
+      ;;
+  esac
+
+  debug "Pinged $machine; result = $ping_result; state = $state"
+
+  # Output based on state
+  if [ $state -eq 0 ]; then
+    if [ "$up" = "true" ]; then
+      let total_up=total_up+1
+      display "${up_color}Up${normal}"
+    fi
+  else
+    if [ "$up" = "false" ]; then
+      display "$machine\t\c"
+      if [ ${#machine} -lt 8 ]; then
+        display "\t\c"
+      fi
+    fi
+    if [ $state -eq 1 ]; then
+      let total_down=total_down+1
+      display "${down_color}Down${normal}"
+    else
+      let total_weak=total_weak+1
+      display "${limping_color}Weak response${normal}"
+    fi
+  fi
+done
+
+print_totals
diff --git a/clients/HP/bin/wrkservers b/clients/HP/bin/wrkservers
new file mode 100644
index 0000000..003833e
--- /dev/null
+++ b/clients/HP/bin/wrkservers
@@ -0,0 +1,40 @@
+#!/bin/ksh
+################################################################################
+#
+# File:         wrkservers
+# RCS:          $Header: wrkservers,v 1.1 98/01/27 22:31:43 defaria Exp $
+# Description:  A script to execute a command on all virtual workstation
+#               servers.
+# Author:       Andrew DeFaria, California Language Labs
+# Created:      Wed Mar  5 16:31:13 PST 1997
+# Modified:     Fri Jan 16 13:54:53 PST 1998
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+PATH=/adm/bin:$PATH
+
+if [ "$1" = "-r" ]; then
+  root=yes
+  shift
+fi
+
+for wrkserver in $(get_info server_names virtualws); do
+  # Execute command. Note if no command is given then the effect is to
+  # rlogin to each machine.
+  print "$wrkserver:$@"
+  if [ $# -gt 0 ]; then
+    if [ -z "$root" ]; then
+      remsh $wrkserver -n "$@"
+    else
+      root remsh $wrkserver -n "$@"
+    fi
+  else
+    if [ -z "$root" ]; then
+      remsh $wrkserver
+    else
+      root remsh $wrkserver
+    fi
+  fi
+done
diff --git a/clients/HP/cygwin_setup b/clients/HP/cygwin_setup
new file mode 100755
index 0000000..d013848
--- /dev/null
+++ b/clients/HP/cygwin_setup
@@ -0,0 +1,241 @@
+#!/bin/bash
+################################################################################
+#
+# File:         cygwin_setup
+# Description:  This script will perform additional setup to configure the
+#		local machine into the cygwin enviornment for Salira
+# Author:       Andrew@DeFaria.com
+# Created:      Fri Oct  5 15:30:16  2001
+# Modified:
+# Language:     Bash Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+# Set me to command name
+me=$(basename $0)
+
+# Global variables
+commonserver=sonscentral
+commonarea=common
+adm=//$commonserver/$commonarea/adm
+homeserver=sonscentral
+homeshare=users
+ccserver=sons-clearcase
+anonymous_ftp_server=sons-clearcase
+viewshare=views
+printerserver=sons-mrp
+printers="\
+  LJ4050PCL6\
+  LJ45500-Color\
+  LJ8150\
+"
+defaultprinter=LJ8150
+
+# Current machine's OS.
+OS=$(uname -s | cut -f2 -d-)
+
+# Current machine's hostname
+hostname=$(echo $(hostname) | tr [:upper:] [:lower:])
+
+# Setup standard mounts
+#
+# Home directory
+echo "Step 1 of 10: Setting up /home mount point"
+mount -tsf //$homeserver/$homeshare /home
+
+# Clearcase views
+echo "Step 2 of 10: Setting up /view mount point"
+if [ $hostname = $ccserver ]; then
+  mount -tsf C:/ClearCaseStorage/Views /view
+else
+  mount -tsf //$ccserver/$viewshare /view
+fi
+
+# Set cygdrive prefix to /dev
+echo "Step 3 of 10: Setting cygdrive-prefix to /dev"
+mount -s --change-cygdrive-prefix /dev
+
+# Remove user level cygdrive-prefix (Need to do this with regedit
+regedit /s \\\\$commonserver\\$commonarea\\FixCygwin.reg 
+
+# Link passwd file
+echo "Step 4 of 10: Create common password file"
+if [ ! -f /etc/passwd.local ]; then
+  if [ ! -L /etc/passwd ]; then
+    cp /etc/passwd /etc/passwd.local
+  fi
+fi
+
+if [ ! -L /etc/passwd ]; then
+  if [ "$OS" != "4.0" ]; then
+    rm /etc/passwd
+    ln -s //$commonserver/$commonarea/passwd /etc/passwd
+  else
+    cp //$commonserver/$commonarea/passwd /etc/passwd
+  fi
+else
+  if [ "$OS" = "4.0" ]; then
+    # Fix up NT 4.0 machines (they don't like symlinked /etc/passwd files!)
+    rm /etc/passwd
+    cp //$commonserver/$commonarea/passwd /etc/passwd
+  fi
+fi
+
+# Link group file
+echo "Step 5 of 10: Create common group file"
+if [ ! -f /etc/group.local ]; then
+  if [ ! -L /etc/group ]; then
+    cp /etc/group /etc/group.local
+  fi
+fi
+
+if [ ! -L /etc/group ]; then
+  rm /etc/group
+  ln -s //$commonserver/$commonarea/group /etc/group
+fi
+
+# Link /etc/profile
+echo "Step 6 of 10: Linking /etc/profile to common profile file"
+if [ ! -f /etc/profile.orig ]; then
+  if [ ! -L /etc/profile ]; then
+    cp /etc/profile /etc/profile.orig
+  fi
+fi
+
+if [ ! -L /etc/profile ]; then
+  rm /etc/profile
+  ln -s //$commonserver/$commonarea/profile /etc/profile
+fi
+
+# Setup printer mount
+echo "Step 7 of 10: Setting up printers"
+for printer in $printers; do
+  mount -bsf //$printerserver/$printer /dev/$printer
+done
+
+# Mount default printer
+mount -bsf //$printerserver/$defaultprinter /dev/lp
+
+# Install internet services
+echo "Step 8 of 10: Installing internet services"
+
+# First save any pre-existing /etc/motd
+if [ -f /etc/motd ]; then
+  cp /etc/motd /etc/motd.$$
+fi
+
+rm -f /etc/ftpusers /etc/ftpwelcome /etc/inetd.conf /etc/motd /etc/shells
+iu-config > /dev/null
+
+# In order to allow anonymous ftp access we need to clear /etc/ftpusers.
+# Do this only for the $anonymous_ftp_server for now
+if [ $hostname = $anonymous_ftp_server ]; then
+  cat /dev/null > /etc/ftpusers
+fi
+
+# Now replace that saved /etc/motd if it existed, otherwise remove the boring
+# /etc/motd that iu-config creates. First check to see if the user has a 
+# personalized /etc/motd in /etc/motd.save
+if [ -f /etc/motd.save ]; then
+  # User had a personalized motd so move it into place and remove any prior
+  # copies
+  mv /etc/motd.save /etc/motd
+  rm -f /etc/motd.$$
+elif [ -f /etc/motd.$$ ]; then
+  # Reinstall previous motd
+  # First update uname -a line
+  uname -a > /etc/motd
+
+  # Remove old uname -a line if present
+  grep -ve "^cygwin" /etc/motd.$$ >> /etc/motd.$$
+
+  # Cleanup
+  rm -f /etc/motd.$$
+else
+  # No saved motd or previous motd. Remove /etc/motd which will cause us
+  # to prompt for the information later.
+  rm /etc/motd
+fi
+
+# Need to hardlink /usr/bin/cygwin1.dll & /usr/sbin/cygwin1.dll
+# 12/17/2001: Stopped hardlinking cygwin1.dll. Enforcing having Windows system
+# environment variables instead. For this we need Cygwin's bin in the path. 
+# User should also set CYGWIN=ntsec in a Windows system environment variable.
+if [ -f /usr/sbin/cygwin1.dll ]; then
+  rm -f /usr/sbin/cygwin1.dll
+  #ln /usr/bin/cygwin1.dll /usr/sbin/cygwin1.dll
+  echo "Warning: Please make sure that you have a Windows *SYSTEM* environment"
+  echo "         variable named CYGWIN set to the value of \"ntsec\" and that"
+  echo "         you have \bin inserted into the Windows *SYSTEM*"
+  echo "         environment variable named PATH"
+fi
+
+# Set up anonymous ftp iff we are on the $anonymous_ftp_server
+if [ $hostname = $anonymous_ftp_server ]; then
+  # Toggle on write access to ~ftp/bin
+  chmod +w ~ftp/bin
+
+  # Remove old copies of ls and cygwin1.dll
+  rm -f ~ftp/bin/ls.exe 
+  rm -f ~ftp/bin/cygwin1.dll
+
+  # Install new copies (Note hardlinks will not work here since ~ftp/bin is
+  # on another file system. Doing an ln simply does a copy anyway)
+  # 12/17/2001: Skipping copying of cygwin1.dll as noted above
+  cp /bin/cygwin1.dll ~ftp/bin/cygwin1.dll
+  cp /bin/ls.exe ~ftp/bin/ls.exe
+
+  # Set security
+  chmod 555 ~ftp/bin/cygwin1.dll
+  chmod 111 ~ftp/bin/ls.exe
+  chown Administrator ~ftp/bin/cygwin1.dll 
+  chown Administrator ~ftp/bin/ls.exe
+  chmod -w ~ftp/bin
+fi
+
+# Install inetd as a service
+/usr/sbin/inetd --install-as-service
+
+# Start inetd service
+inetd_started=$(net start | grep -i inetd)
+
+if [ -z "$inetd_started" ]; then
+  net start inetd
+fi
+
+# Setup SMTP
+$adm/bin/setup_ssmtp
+
+# Setup cron
+$adm/bin/setup_cron
+
+# Create /etc/motd
+echo "Step 9 of 10: Gathering machine specific information"
+if [ ! -f /etc/motd ]; then
+  $adm/bin/make_motd
+  made_motd=true
+else
+  echo "Skipped: Machine info already gathered"
+fi
+
+# Fixup /etc/ftpwelcome
+host=$(hostname | tr [:upper:] [:lower:])
+echo "Welcome to $host's ftp service" > /etc/ftpwelcome
+
+# Update machines file
+echo "Step 10 of 10: Registering this machine (This takes a few seconds)"
+if [ ! -z "$made_motd" ]; then
+  $adm/bin/update_machine_info
+else
+  echo "Skipped: Machine already registered"
+fi
+
+# Sneaky other fixes...
+# Link /bin/more.exe -> /bin/less.exe
+if [ ! -L /bin/more.exe ]; then
+  ln -s /bin/less.exe /bin/more.exe
+fi
+
+# Finished
+echo "Done"
diff --git a/clients/HP/daily b/clients/HP/daily
new file mode 100755
index 0000000..ae93e19
--- /dev/null
+++ b/clients/HP/daily
@@ -0,0 +1,151 @@
+#!/bin/ksh
+################################################################################
+#
+# File:         daily
+# Description:  This is the daily cronjob for root
+# Author:       Andrew@DeFaria.com
+# Created:      Wed Jul 21 12:12:28 PDT 1999
+# Modified:
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+me=$(basename $0)
+
+# Set adm_base
+adm_base=${adm_base:-$HOME/adm}
+
+# Set adm_fpath
+adm_fpath=${adm_fpath:-$adm_base/functions}
+
+# Source functions
+. $adm_fpath/common
+
+# Add $adm_base/bin and $adm_base/clearcase PATH
+export PATH=$adm_base/bin:$adm_base/clearcase:$PATH
+
+# Source in tmpfiles function
+tmpprefix=${TMPDIR:-/tmp}/$me.$$
+tmpfile=$tmpprefix
+. $adm_fpath/tmpfiles
+arm_trap
+
+# Where logs are kept
+logs=$adm_host/logs
+
+# Define admin_host. Admin_host is the machine where checks for the network
+# as a whole are run (such as check_view_storage)
+admin_host=dreamcicle # For now...
+
+verbose=
+debug=
+
+function usage {
+  display "$me [-v|verbose] [-d|debug] [-u|usage] [-n|notify ]"
+  display "        -v|verbose:     Turns on verbose mode"
+  display "        -d|debug:       Turns on debug mode"
+  display "        -u|usage:       Print this usage message\n"
+  display "        -n|notify:      Who to notify of problems (default root)"
+
+  error "$1" 1
+} # usage
+
+lab_admin=cdsadmin # For now
+local_admin=cdsadmin
+
+function notify {
+  debug "ENTER notify"
+  who=$1
+  logfile=$2
+
+  cat > $tmpfile <> $tmpfile
+
+  mailx -s "Notice: $me cronjob discovered the following problems" $who <
+$tmpfile
+  debug "EXIT notify"
+} # notify
+
+# Get parameters
+while [ $# -ge 1 ]; do
+  case "$1" in
+    -usage)
+      usage
+    ;;
+
+    -v|-verbose)
+      verbose=yes
+    ;;
+
+    -d|-debug)
+      debug=yes
+    ;;
+
+    -n|-notify)
+      shift
+      if [ $# -lt 1 ]; then
+        error "Notify email address was unspecified!" 1
+      fi
+      local_admin="$1"
+    ;;
+
+    *)
+      usage "Unrecognized parameter $1"
+    ;;
+  esac
+  shift
+done
+
+## Main
+# Some common portions of log filenames:
+host=$(uname -n) # System's name
+date=$(date +%d) # Note we keep one month of rolling logs
+
+if [ "$host" = "$admin_host" ]; then
+  network_diskspace_log=$logs/network.diskspace.$date.log
+  verbose "Diskspace Report -> $network_diskspace_log"
+  diskspace -network > $network_diskspace_log 2>&1
+
+  if [ -s $network_diskspace_log ]; then
+    notify $lab_admin $network_diskspace_log
+  fi
+
+  view_storage_log=$logs/viewstorage.$date.log
+  verbose "View Storage Report -> $view_storage_log"
+  check_view_storage > $view_storage_log 2>&1
+
+  if [ -s $view_storage_log ]; then
+    notify $lab_admin $view_storage_log
+  fi
+
+  # Produce a viewspace report for all production view servers
+  viewservers="cds-sundev-rem canon"
+
+  for viewserver in $viewservers; do
+    viewspace_log=$logs/$viewserver.viewspace.$date.log
+    verbose "Viewspace Report for $viewserver -> $viewspace_log"
+    viewspace -host $viewserver > $viewspace_log
+  done
+fi
+
+# Checks run on all machines
+local_diskspace_log=$logs/$host.diskspace.$date.log
+verbose "Diskspace Report -> $local_diskspace_log"
+diskspace -local > $local_diskspace_log 2>&1
+
+if [ -s $local_diskspace_log ]; then
+  notify local_admin $local_diskspace_log
+fi
+
+machine_configuration_log=$logs/$host.machine_configuration.$date.log
+verbose "Machine Configuration Report -> $machine_configuration_log" 2>&1 &
+configure_machine -f > $machine_configuraton_log 2>&1
+
+if [ -s $machine_configuration_log ]; then
+  notify local_admin $machine_configuration_log
+fi
diff --git a/clients/HP/packet2vob b/clients/HP/packet2vob
new file mode 100755
index 0000000..fee265e
--- /dev/null
+++ b/clients/HP/packet2vob
@@ -0,0 +1,37 @@
+#!/usr/bin/bash
+################################################################################
+#
+# File:         packet2vob
+# RCS:          $Header: packet2vob,v 1.1 97/04/23 13:11:08 defaria Exp $
+# Description:  A script to display what vob the packet is for
+# Author:       Andrew DeFaria, California Language Labs
+# Created:      Fri Feb 28 07:53:14 PST 1997
+# Modified:     
+# Language:     Korn Shell
+# Package:      N/A
+# Status:       Experimental (Do Not Distribute)
+#
+# (c) Copyright 1995, Hewlett-Packard Company, all rights reserved.
+#
+################################################################################
+alias ct=/usr/atria/bin/cleartool
+alias mt=/usr/atria/bin/multitool
+
+if [ $# -eq 1 ]; then
+  packets="*"
+else
+  packets="$@"
+fi
+
+if [ ! -f $RGY/vob_object ]; then
+  print -u2 "Unable to to interogate the registry ($RGY/vob_object)"
+  exit 1
+fi
+
+for packet in $packets; do
+  familyid=$(mt lspacket $packet 2> /dev/null | grep "family" | awk '{print $5}')
+  uuid=$(grep $familyid $RGY/vob_object | cut -f4 -d';' | cut -c14-)
+  vob=$(ct lsvob -long -uuid $uuid | grep "Tag:" | awk '{print $2}')
+  host=$(ct lsvob -long -uuid $uuid | grep "Server host:" | awk '{print $3}')
+  print "$packet $host:$vob"
+done
diff --git a/clients/HP/pingnet b/clients/HP/pingnet
new file mode 100755
index 0000000..9d0383f
--- /dev/null
+++ b/clients/HP/pingnet
@@ -0,0 +1,115 @@
+#!/bin/ksh
+################################################################################
+#
+# File:         pingnet
+# RCS:          $Header: pingnet,v 1.3 98/03/04 00:42:49 defaria Exp $
+# Description:  A script to ping all machines and report status. This script
+#               uses /etc/hosts and selects only machines in the IP range of
+#               15.0.96.x to 15.0.99.x.
+# Author:       Andrew DeFaria 
+# Created:      Sat Oct 26 10:04:28 PDT 1996
+# Modified:     Sat Oct 26 12:05:26 PDT 1996 Andrew DeFaria 
+# Parameters:   count (default 2): number of times to ping a machine before
+#               considering it not responding.
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+me=$(basename $0)
+
+function usage {
+  print "$me: Usage: $me { count }"
+  print "\twhere count = number of times to ping a machine before
+considering"
+  print "\tthe machine to be not responding (default count = 2)."
+  exit 1
+} # usage
+
+os=
+
+function get_os {
+  machine=$1
+  # Attempt to determine the OS. First attempt to remsh to the machine and
+  # do a uname(1). This assumes Unix. We are unable to remsh then we'll
+  # assume that it's a PC (it could also be a line printer or any other of
+  # a number of network pingable devices!)
+  os=$(remsh $machine -n uname 2>&1)
+
+  # We're gonna make some guesses here...
+  if [[ "$os" = "HP-UX" ||
+        "$os" = "Linux" ]]; then
+    : Do nothing!
+  elif [[ "$os" = *Lost\ connection ]]; then
+    os="Linux? (Lost connection)"
+  elif [[ "$os" = *Permission\ denied. ]]; then
+    os="Linux? (Permission denied)"
+  elif [[ "$os" = *Login\ incorrect ]]; then
+    os="HP-UX? (Login incorrect)"
+  elif [[ "$os" = *Connection\ refused ]]; then
+    os="NT? (Connection refused)"
+  else
+    os="Unknown: $os"
+  fi
+} # get_os
+
+integer count=2
+
+if [ $# -eq 1 ]; then
+  count=$1
+elif [ $# -gt 1 ]; then
+  usage
+fi
+
+esc=$(print "\033")
+
+if [ "$TERM" = "dtterm" -o -z DTTERM ]; then
+  export normal="$esc[39m"
+  export red="$esc[31m"
+  export green="$esc[32m"
+elif [ "$TERM" = "hp" -o "$TERM" = "hpterm" ]; then
+  export normal="$esc&d@$esc&v0S"
+  export red="$esc&v1S"
+  export green="$esc&v2S"
+fi
+
+# Print heading
+print "       Machine\t\t      IP\tState\t  OS"
+print - "-----------------------\t-------------\t-----\t-------"
+
+subnet=${subnet:-15.28}
+integer starting_subnet_octect=${starting_subnet_octect:-96}
+integer ending_subnet_octect=${ending_subnet_octect:-103}
+integer subnet_octect=starting_subnet_octect
+integer starting_octect=${starting_octect:-0}
+integer ending_octect=${ending_octect:-255}
+integer octect=$starting_octect
+
+while [ $subnet_octect -le $ending_subnet_octect ]; do
+  while [ $octect -le $ending_octect ]; do
+    ip=$subnet.$subnet_octect.$octect
+    machine=$ip
+    nslookup $ip 2>&1 | grep Non-exist > /dev/null 2>&1
+    if [ $? -ne 0 ]; then
+      machine=$(nslookup $ip | grep Name: | awk '{print $2}' | cut -f1 -d.)
+    fi
+    print "$machine\t\c"
+    if [ ${#machine} -lt 8 ]; then
+      print "\t\t\c"
+    elif [ ${#machine} -lt 16 ]; then
+      print "\t\c"
+    fi
+    print "$ip\t\c"
+    ping_result=$(ping $ip -n $count | tail -1 | grep "100% packet loss")
+    if [ -z "$ping_result" -eq 0 ]; then
+      print "${green}UP${normal}\t\c"
+      get_os $machine
+      print "$os"
+    else
+      print "${red}DOWN${normal}\tUnknown"
+    fi
+    let octect=octect+1
+  done
+  let subnet_octect=subnet_octect+1
+  let octect=starting_octect
+done
diff --git a/clients/HP/roll_logs b/clients/HP/roll_logs
new file mode 100755
index 0000000..b38dc79
--- /dev/null
+++ b/clients/HP/roll_logs
@@ -0,0 +1,115 @@
+#!/bin/ksh
+################################################################################
+#
+# File:         roll_logs
+# Description:  Rolls log files
+# Author:       Andrew@DeFaria.com
+# Created:      Thu Dec  9 10:05:09 PST 1999
+# Modified:
+# Language:     Korn Shell
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+# Set me to command name
+me=$(basename $0)
+
+# Set adm_base
+adm_base=${adm_base:-$HOME/adm}
+
+# Set adm_fpath
+adm_fpath=${adm_fpath:-$adm_base/functions}
+
+# Source functions
+. $adm_fpath/common
+. $adm_fpath/logs
+
+function usage {
+  if [ "_$1" != "_" ]; then
+    display "$1"
+    display
+  fi
+  display "Usage: $me -[da|aily] | -[w|eekly]"
+  exit 1
+} # usage
+
+# Check for execution by root
+if is_not_root; then
+  error "This script must be run as root" 1
+fi
+
+type=
+while [ $# -ge 1 ]; do
+  case "$1" in
+    -usage)
+      usage
+    ;;
+
+    -v|-verbose)
+      verbose=yes
+    ;;
+
+    -d|-debug)
+      debug=yes
+    ;;
+
+    -da|-daily)
+      type=daily
+      ;;
+
+    -w|-weekly)
+      type=weekly
+      ;;
+
+    *)
+      usage "Unrecognized parameter $1"
+    ;;
+  esac
+  shift
+done
+
+if [ "_$type" = "_" ]; then
+  usage "Must specify -daily or -weekly"
+fi
+
+#Directory      logfile         backuplog               what
+dailylogs="\
+/var/adm        automount.log   autolog.week            automount\n\
+/var/adm        nettl.LOG00     nettl.week              nettracelog\n\
+/var/adm        ninstall.log    nlog.week               ninstall\n\
+/var/adm        ptydaemonlog    ptylog.week             ptydaemon\n\
+/var/adm        rpc.lockd.log   rpc.lockd.week          rpc.lockd\n\
+/var/adm        rpc.statd.log   rpc.statd.week          rpc.statd\n\
+/var/adm        vtdaemonlog     vtlog.week              vtdaemon\n\
+/var/adm/cron   log             log.week                cron\n\
+/var/adm/lp     log             log.week                lp\n\
+/var/adm/syslog syslog.log      syslog.week             syslogd"
+
+weeklylogs="\
+/var/adm        autolog.week    autolog.oldweek         automount\n\
+/var/adm        nettl.week      nettl.oldweek           nettracelog\n\
+/var/adm        nlog.week       nlog.oldweek            ninstall\n\
+/var/adm        ptylog.week     ptylog.oldweek          ptydaemon\n\
+/var/adm        rpc.lockd.week  rpc.lockd.oldweek       rpc.lockd\n\
+/var/adm        rpc.statd.week  rpc.statd.oldweek       rpc.statd\n\
+/var/adm        vtlog.week      vtlog.oldweek           vtdaemon\n\
+/var/adm/cron   log.week        log.oldweek             cron\n\
+/var/adm/lp     log.week        log.oldweek             lp\n\
+/var/adm/syslog syslog.week     syslog.oldweek          syslogd"
+
+if [ "$type" = "daily" ]; then
+  verbose "Daily roll_logs"
+  logfiles="$dailylogs"
+else
+  verbose "Weekly roll_logs"
+  logfiles="$weeklylogs"
+fi
+
+print "$logfiles" | while read dir logfile backup_logfile what; do
+  verbose "Rolling ($what) logfile $logfile -> $backup_logfile..."
+  if [ "$type" = "weekly" ]; then
+    # Clear out oldweek file first
+    rm -f $backup_logfile
+  fi
+  roll_log $dir $logfile $backup_logfile $what
+done
diff --git a/clients/HP/setup_cron b/clients/HP/setup_cron
new file mode 100755
index 0000000..fe1878e
--- /dev/null
+++ b/clients/HP/setup_cron
@@ -0,0 +1,28 @@
+#!/bin/bash
+################################################################################
+#
+# File:         setup_cron
+# Description:  This script sets up Cygwin's cron on the local machine
+# Author:       Andrew@DeFaria.com
+# Created:      
+# Language:     Bash Shell
+# Modifications:
+#
+# (c) Copyright 2002, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+me=$(basename $0)
+# Make sure that certain directories and files do not exist! This is to let
+# cron create them, which appears to be the only way to get these created
+# correctly! 
+if [ ! -d /var/cron ]; then
+  rm -rf /var/cron
+  rm -rf /var/run/cron.pid
+  rm -rf /var/log/cron.log
+
+  # Install cron service:
+  cygrunsrv -I cron -p /usr/sbin/cron -a -D -d "Cygwin cron" -e "MAILTO=$USER@Salira.com" -e "CYGWIN=ntsec"
+fi
+
+# Start cron service
+cygrunsrv -S cron
diff --git a/clients/HP/setup_ssmtp b/clients/HP/setup_ssmtp
new file mode 100755
index 0000000..726eeed
--- /dev/null
+++ b/clients/HP/setup_ssmtp
@@ -0,0 +1,58 @@
+#!/bin/bash
+################################################################################
+#
+# File:         setup_ssmtp
+# Description:  This script sets up ssmtp mail configuration
+# Author:       Andrew@DeFaria.com
+# Created:      Wed Jan  9 12:57:13  2002
+# Language:     Bash Shell
+# Modifications:
+#
+# (c) Copyright 2002, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+# Setup /etc/ssmtp config directory
+ssmtp_dir=/etc/ssmtp
+domain=${domain:-defaria.com}
+mail_server=10.1.1.101
+
+mkdir -p $ssmtp_dir
+chmod 700 $ssmtp_dir
+
+# Make some simple aliases. Alias $USER to the proper email address and then
+# alias root, Administrator and postmaster to the user's address thus making
+# the user "god" of smtp on this machine only.
+cat > $ssmtp_dir/revaliases < $ssmtp_dir/ssmtp.conf <rel2abs ($1);
+  $me           = (!defined $2) ? $0  : $2;
+  $me		=~ s/\.pl$//;
+
+  # Setup paths
+  $bin_path             = "$abs_path";
+  $lib_path             = "$abs_path/../lib";
+  $log_path             = "$abs_path/../log";
+
+  # Add the appropriate path to our modules to @INC array.
+  unshift (@INC, "$lib_path");
+} # BEGIN
+
+use ecrc;
+
+# Global variables
+my $servername		= (!defined $ENV {ECRDSERVER}) ? "lynx12" : $ENV {ECRDSERVER};
+my $port		= (!defined $ENV {ECRDPORT})   ? 1500     : $ENV {ECRDPORT};
+my $ecr			= "";
+my @query_fields	= ();
+my $verbose;
+my $debug;
+my $key;
+my $value;
+my %fields;
+my @ecrs;
+
+sub Usage {
+  my $msg = shift;
+
+  print "ERROR: $msg\n\n" if defined $msg;
+
+  print "Usage: ecrc [-u] [-v] [-d] [ -s  ] [ -p  ] ";
+  print "ECR [ fieldname... ]\n";
+  print "\nWhere:\n\n";
+  print "\t-u:\t\tDisplay usage\n";
+  print "\t-v:\t\tTurn on verbose mode (Default off)\n";
+  print "\t-d:\t\tTurn on debug mode (Default off)\n";
+  print "\t-s:\t\tUse server named servername (Default lynx12)\n";
+  print "\t-s:\t\tUse port (Default 1500)\n";
+  print "\tECR:\t\tECR number to obtain info about\n";
+  print "\tfieldname:\tECR field names to retrieve info about (Default all)\n";
+
+  exit 1;
+} # Usage
+
+sub GetParms {
+  while ($ARGV [0]) {
+    if ($ARGV [0] eq "-v") {
+      $verbose		= 1;
+      ecrc::set_verbose;
+    } elsif ($ARGV [0] eq "-d") {
+      $debug 		= 1;
+      ecrc::set_debug;
+    } elsif ($ARGV [0] eq "-u") {
+      Usage;
+    } elsif ($ARGV [0] eq "-p") {
+      shift @ARGV;
+      Usage "Port not specified" if !$ARGV [0];
+      $port = shift @ARGV;
+    } elsif ($ARGV [0] eq "-s") {
+      shift @ARGV;
+      Usage "Server name not specified" if !$ARGV [0];
+      $servername = shift @ARGV;
+    } else {
+      $ecr = shift (@ARGV);
+      last;
+    } # if
+    shift @ARGV;
+  } # while
+
+  @query_fields = @ARGV;
+
+  # Downshift any query_fields
+  my $i = 0;
+
+  foreach (@query_fields) {
+    $query_fields [$i++] = lc $_;
+  } # foreach
+} # GetParms
+
+# Main code
+GetParms;
+
+die "Unable to connect to $servername:$port\n" if !ecrc::Connect ($servername, $port);
+
+if ($ecr) {
+  if ($ecr eq "\*") {
+    @ecrs = ecrc::GetECRRecord $ecr;
+
+    foreach (@ecrs) {
+      print "$_\n";
+    } # foreach
+
+    exit;
+  } # if
+
+  %fields = ecrc::GetECRRecord ($ecr);
+
+  if (!%fields) {
+    print "ECR $ecr was not found\n";
+  } else {
+    if (@query_fields) {
+      foreach (@query_fields) {
+	if (@query_fields > 1) {
+	  if (defined $fields{$_}) {
+	    print "$_: $fields{$_}\n";
+	  } else {
+	    print "$_: \n";
+	  } # if
+	} else {
+	  if (defined $fields{$_}) {
+	    print "$fields{$_}\n";
+	  } else {
+	    print "$_: \n";
+	  } # if
+	} # if
+      } # foreach
+    } else {
+      while (($key, $value) = each (%fields)) {
+	print "$key: $value\n";
+      } # while
+    } # if
+  } # if
+} else {
+  print "Enter ECR:";
+
+  while (my $command = ) {
+    chomp $command;
+    last if $command =~ m/exit|quit/i;
+
+    $ecr = $command;
+
+    if ($ecr eq "\*") {
+      my @ecrs = ecrc::GetECRRecord $ecr;
+
+      foreach (@ecrs) {
+	print "$_\n";
+      } # foreach
+    } else {
+      %fields	= ecrc::GetECRRecord $ecr;
+
+      if (!%fields) {
+	print "ECR $ecr was not found\n";
+      } else {
+	while (($key, $value) = each (%fields)) {
+	  print "$key: $value\n";
+	} # while
+      } # if
+    } # if
+
+      print "Enter ECR:";
+  } # while
+} # if
diff --git a/clients/LynuxWorks/bin/ecrd b/clients/LynuxWorks/bin/ecrd
new file mode 100644
index 0000000..40e4057
--- /dev/null
+++ b/clients/LynuxWorks/bin/ecrd
@@ -0,0 +1,564 @@
+#!/usr/bin/perl
+################################################################################
+#
+# File:		ecrd: ECR Daemon
+# Description:  This script implements a daemon that handles requests for
+#		queries about information on ECRs contained in the Quintus
+#		database. In addition to lessoning the amount of time it takes
+#		for database opens, access to Quintus data is only available
+#		on certain machines. Additionally, for Perl to access this
+#		Informix database the Informix version of DBD would need to be
+#		locally installed. By calling this daemon instead clients need
+#		not have to install Informix and then code all the necessary
+#		code to access Quintus data as well as have to understand the
+#		structure of the database. Instead clients need only say "Give
+#		me what you got on ECR #".
+# Author:       Andrew@DeFaria.com
+# Created:      Tue Feb 15 09:54:59 PST 2005
+# Modified:
+# Language:     Perl
+#
+# (c) Copyright 2005, LynuxWorks, all rights reserved.
+#
+################################################################################
+use strict;
+use warnings;
+
+use IO::Socket;
+use Net::hostent;
+use POSIX qw(setsid);
+use DBI;
+
+my $ecrdb    = "lynxmigr1";
+my $port     = (!defined $ENV {ECRDPORT}) ? 1500 : $ENV {ECRDPORT};
+
+# Global variables
+my $DB;
+my $ecrserver;
+my $ecrclient;
+my $sth;
+my $statement;
+
+# Options
+my $verbose;
+my $debug;
+my $daemon_mode;
+my $quiet_mode;
+my $multithreaded;
+my $timeout	= 10;
+
+# ECR translations. Note the Quintus database stores certain choice lists as 
+# enumerations. They I guess they are configurable. The right thing to do 
+# would be to figure out how to look up the definition given the number. But
+# we are gonna cheat here and hard code a few important enumerations.
+my @defstatus = (
+  "Open",
+  "Closed",
+  "Fixed",
+  "Not a bug",
+  "Obsolete",
+  "Defered",
+  "Duplicate"
+);
+my @state = (
+  "Reported",
+  "Assigned",
+  "Selected",
+  "Resolved",
+  "Integrated",
+  "Retired",
+  "Reviewed",
+  "Pending Review"
+);
+my @priority = (
+  "Low",
+  "Medium",
+  "High",
+  "Critical"
+);
+my @severity = (
+  "Low",
+  "Medium",
+  "High",
+  "Critical"
+);
+
+# Pid
+my $pid = $$;
+
+my $me = `basename $0`;
+chomp $me;
+my $ecrdversion = "1.3";
+
+my @all_fields = (
+  "productdefect",	# integer
+  "componentdefect",	# integer
+  "defectdefectdup",	# integer
+  "workgroupdefect",	# integer
+  "reporterdefect",	# integer
+  "resolverdefect",	# integer
+  "confirmerdefect",	# integer
+  "buildversdefect",	# integer
+  "rpt_versdefect",	# integer
+  "res_versdefect",	# integer
+  "conf_versdefect",	# integer
+  "state",		# integer
+  "resolverstatus",	# integer
+  "confirmerstatus",	# integer
+  "escstatus",		# integer
+  "owner",		# integer
+  "severity",		# integer
+  "priority",		# integer
+  "summary",		# varchar(80,0)
+  "datereported",	# datetime year to second
+  "dateresolved",	# datetime year to second
+#  "description",	# text
+# Note: Some descriptions fields are huge containing things like
+# uuencoded tar files!  They are so huge that they cause this server
+# to fail (not sure why - it shouldn't but it does. So this hack
+# returns only the first 50K of description to avoid that problem.
+  "description [1,50000]",	# text
+  "cclist",		# varchar(80,0)
+  "dateconfirmed",	# datetime year to second
+  "datemodified",	# datetime year to second
+  "fix_by_date",	# date
+  "fix_by_version",	# integer
+  "history",		# text
+  "likelihood",		# integer
+  "estfixtime",		# datetime year to second
+  "actfixtime",		# datetime year to second
+  "resolution",		# text
+  "businessimpact",	# integer
+  "origin",		# integer
+  "docimpact",		# integer
+  "report_platform",	# integer
+  "resolve_platform",	# integer
+  "confirm_platform",	# integer
+  "test_file",		# varchar(64,0)
+  "visibility",		# integer
+  "misc",		# varchar(80,0)
+  "defecttype",		# integer
+  "defstatus",		# integer
+  "customertext",	# text
+  "modifiedby",		# varchar(20,0)
+  "classification",	# integer
+  "datefixed"		# datetime year to second
+);
+
+# Forwards
+sub CloseDB;
+sub GetRequest;
+
+sub timestamp {
+  my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
+
+  $mday  = "0$mday" if $mday < 10;
+  $mon   = "0$mon"  if $mon  < 10;
+  $hour  = "0$hour" if $hour < 10;
+  $min   = "0$min"  if $min  < 10;
+  $year += 1900;
+
+  return "$mon/$mday/$year $hour:$min";
+} # timestamp
+
+sub log_message {
+  print "[$pid] " . timestamp . " @_\n" if defined $verbose;
+} # log_message
+
+sub log_error {
+  print STDERR "[$pid] " . timestamp . " ERROR: @_\n"
+} # log_error
+
+sub log_warning {
+  print STDERR "[$pid] " . timestamp . " WARNING: @_\n"
+} # log_error
+
+sub debug {
+  print "[$pid] " . timestamp . " @_\n" if defined $debug;
+} # debug
+
+sub verbose {
+  print "[$pid] " . timestamp . " @_\n" if !defined $quiet_mode;
+} # verbose
+
+sub DBError {
+  my $msg       = shift;
+  my $statement = shift;
+
+  if (!defined $DB) {
+    print "Catostrophic error: DB undefined!\n";
+    exit 1;
+  } # if
+
+  print $msg . "\nError #" . $DB->err . " " . $DB->errstr . "\n";
+  print "SQL Statement: $statement\n" if defined $statement;
+
+  exit $DB->err;
+} # DBError
+
+sub timeout {
+  debug "After $timeout seconds of inactivity client timed out";
+
+  my $hostinfo = gethostbyaddr ($ecrclient->peeraddr);
+  my $host = $hostinfo->name || $ecrclient->peerhost;
+  debug "Closing connection to $host";
+
+  # Close client's connection
+  close $ecrclient;
+
+  # Set up signal handlers again
+  $SIG{ALRM} = \&timeout;
+  $SIG{INT}  = $SIG{QUIT} = 23234
+\&interrupt;
+  GetRequest;
+} # timeout
+
+sub interrupt {
+  log_warning "Interrupted - closing down...";
+  close $ecrserver;
+  verbose "Connection closed";
+  CloseDB;
+
+  exit;
+} # interrupt
+
+sub GetClientAck {
+  my $client = shift;
+  my $clientresp;
+
+  debug "ENTER: GetClientAck";
+  alarm $timeout;
+  while (defined $client and defined ($clientresp = <$client>)) {
+    chomp $clientresp;
+    chop $clientresp if $clientresp =~ /\r/;
+    if ($clientresp eq "ACK") {
+      return
+    } # if
+    log_warning "Received $clientresp from client - expected ACK";
+  } # while
+  debug "EXIT: GetClientAck";
+} # GetClientAck
+
+sub GetClientCmd {
+  my $client = shift;
+  my $clientresp;
+
+  alarm $timeout;
+  while (defined $client and defined ($clientresp = <$client>)) {
+    chomp $clientresp;
+    return $clientresp;
+  } # while
+} # GetClientResponse
+
+sub SendClientAck {
+  my $client = shift;
+
+  debug "ENTER: SendClientAck";
+  print $client "ACK\n";
+  debug "EXIT: SendClientAck";
+} # SendClientAck
+
+sub SendClientResponse {
+  my $client   = shift;
+  my $response = shift;
+
+  print $client "$response\n";
+} # SendClientResponse
+
+sub EnterDaemonMode {
+  my $logfile  = shift;
+  my $errorlog = shift;
+
+  $logfile  = "/dev/null" if $logfile  eq "";
+  $errorlog = "/dev/null" if $errorlog eq "";
+
+  # Change the current directory to /
+  chdir '/' 
+    or die "$me: Error: Can't chdir to / ($!)";
+
+  # Turn off umask
+  umask 0;
+
+  # Redirect STDIN to /dev/null
+  open STDIN, '/dev/null'
+    or die "$me: Error: Can't redirect /dev/null ($!)";
+
+  # Redirect STDOUT to logfile
+  open STDOUT, ">>$logfile"
+    or die "$me: Error: Can't redirect stdout to $logfile ($!)";
+
+  # Redirect STDERR to errorlog
+  open STDERR, ">>$errorlog"
+    or die "$me: Error: Can't redirect stderr to $errorlog ($!)";
+
+  # Now fork the daemon
+  defined (my $pid = fork)
+    or die "$me: Error: Can't create daemon ($!)";
+
+  # Now the parent exits
+  exit if $pid;
+
+  # Set process to be session leader
+  setsid
+    or die "$me: Error: Can't start a new session ($!)";
+} # EnterDaemonMode
+
+sub OpenDB {
+  # Connect to database. Note this is using anonymous access (read only)
+  $DB = DBI->connect("DBI:Informix:$ecrdb")
+    or DBError "Unable to open database";
+  log_message "Opened $ecrdb database";
+
+  # Setup our select statement with placeholders
+  $statement = "select ";
+
+  # Build up the field list
+  my $first_time = 1;
+  foreach (@all_fields) {
+    if ($first_time) {
+      $first_time = 0;
+      $statement .= $_;
+    } else {
+      $statement .= ",$_";
+    } # if
+  } # foreach
+
+  # Now add the table and condition
+  $statement .= " from defect where pkey=?";
+
+  $sth = $DB->prepare ($statement)
+    or DBError "Unable to prepare statement", $statement;
+} # OpenDB
+
+sub CloseDB {
+  $DB->disconnect ()
+    or DBError "Unable to disconnect from database!";
+  verbose "Closed $ecrdb database";
+} # CloseDB
+
+sub Usage {
+  my $msg = shift;
+
+  print "$msg\n\n" if defined $msg;
+
+  print "Usage: $me [ -D ] [ -v ] [ -d ] [-p ] [ -m ] [ -q ]\n\n";
+  print "Where:\t-D\tEnter Daemon mode\n";
+  print "\t-v\tVerbose mode (Default off)\n";
+  print "\t-d\tDebug mode (Default off)\n";
+  print "\t-p\tPort number to use (Default 1500)\n";
+  print "\t-m\tMultithreaded (Default off)\n";
+  print "\t-q\tQuiet mode (Default on)\n";
+  exit 1;
+} # Usage
+
+sub GetECRRecord {
+  my $ecr = shift;
+
+  if ($ecr =~ /\D/) {
+    log_error "ECR $ecr is not numeric!";
+    return ();
+  } # if
+
+  my %fields;
+  my $record;
+  my $value;
+
+  $sth->execute ($ecr)
+    or DBError "Unable to execute statement", $statement;
+
+  my $row = $sth->fetchrow_arrayref;
+
+  if (!defined $row) {
+    # @row is empty if there was no ECR by that number
+    log_error "ECR $ecr not found!";
+    return ();
+  } # if
+
+  my @rows = @{$row};
+  foreach (@all_fields) {
+    my $value = shift @rows;
+
+    # Transform newlines to "\n" so the field is treated as one large field
+    $value =~ s/\n/\\n/g if defined $value;
+
+    # Perform some choice list field translations. Again this would be
+    # better done by doing database lookups to translate the enums...
+    $value = $defstatus [$value]	if /defstatus/ and defined $value;
+    $value = $state     [$value]	if /state/     and defined $value;
+    $value = $priority  [$value]	if /priority/  and defined $value;
+    $value = $severity  [$value]	if /severity/  and defined $value;
+    # Fix description field back
+    if (/^description/) {
+      $_ = "description";
+    } # if
+    $fields {$_} = $value
+  } # foreach
+
+  return %fields;
+} # GetECRRecord
+
+sub ServiceClient {
+  my $ecrclient = shift;
+
+  # Service this client
+  my $hostinfo = gethostbyaddr ($ecrclient->peeraddr);
+  my $host = $hostinfo->name || $ecrclient->peerhost;
+
+  verbose "Connect from $host";
+  log_message "Waiting for command from $host";
+  while () {
+    GetClientAck ($ecrclient);
+    $_ = GetClientCmd ($ecrclient);
+    next unless /\S/; # Skip blank requests
+    last if /quit|exit/i;
+
+    if (/\*/) {
+      log_message "$host requests a list of all ECR #'s";
+      SendClientAck ($ecrclient);
+      ReturnAllECRNbrs ($ecrclient);
+      SendClientAck ($ecrclient);
+      next;
+    } # if
+
+    log_message "$host requests information about ECR $_";
+    SendClientAck ($ecrclient);
+    my %fields = GetECRRecord $_;
+
+    if (%fields) {
+      SendClientResponse ($ecrclient, "ecr: $_");
+      while (my ($key, $value) = each (%fields)) {
+	$value = !defined $value ? "" : $value;
+	SendClientResponse ($ecrclient, "$key: $value");
+      } # while
+    } else {
+      SendClientResponse ($ecrclient, "ECR $_ was not found");
+    } # if
+    SendClientAck ($ecrclient);
+  } # while
+
+  verbose "Closing connection from $host at client's request";
+  close $ecrclient;
+} # ServiceClient
+
+sub Funeral {
+  my $childpid = wait;
+  $SIG{CHLD} = \&Funeral;
+  log_message "Child has died" . ($? ? " with status $?" : "");
+} # Funeral
+
+sub GetRequest {
+  # Now wait for an incoming request
+  while ($ecrclient = $ecrserver->accept ()) {
+    my $hostinfo = gethostbyaddr ($ecrclient->peeraddr);
+    my $host = $hostinfo->name || $ecrclient->peerhost;
+    log_message "$host is requesting service";
+    if (defined ($multithreaded)) {
+      my $childpid;
+
+      log_message "Spawning child to handle request";
+
+      die "$me: ERROR: Can't fork: %!" unless defined ($childpid = fork ());
+
+      if ($childpid) {
+	# In parent - set up for clean up of child process
+	log_message "Parent produced child ($childpid)";
+	$SIG{CHLD} = \&Funeral;
+	log_message "Parent looking for another request to service";
+      } else {
+	# In child process - ServiceClient
+	$pid = $$;
+	debug "Child [$pid] has been born";
+	ServiceClient ($ecrclient);
+	log_message "Child finished servicing requests";
+	kill ("TERM", $$);
+	exit;
+      } # if
+    } else {
+      ServiceClient ($ecrclient);
+    } # if
+  } # while
+
+  close ($ecrserver);
+} # GetRequest
+
+sub ProcessRequests {
+  # The subroutine handles processing of requests by using a socket to
+  # communicate with clients.
+  $ecrserver = IO::Socket::INET->new (
+    Proto     => 'tcp',
+    LocalPort => $port,
+    Listen    => SOMAXCONN,
+    Reuse     => 1
+  );
+
+  die "$me: Error: Could not create socket ($!)\n" unless $ecrserver;
+
+  verbose "ECR DB Server (ecrd V$ecrdversion) accepting clients on port $port";
+
+  GetRequest;
+} # ProcessRequests
+
+sub ReturnAllECRNbrs {
+  my $ecrclient = shift;
+
+  my $statement = "select pkey from defect";
+
+  my $sth = $DB->prepare ($statement)
+    or DBError "Unable to prepare statement", $statement;
+
+  $sth->execute ()
+    or DBError "Unable to execute statement", $statement;
+
+  log_message "Returning all ECR numbers...";
+  while (my @row = $sth->fetchrow_array) {
+    SendClientResponse ($ecrclient, $row [0]);
+  } # while
+
+  log_message "All ECR numbers returned";
+} # ReturnAllECRNbrs
+		
+# Start main code
+# Reopen STDOUT.
+open STDOUT, ">-" or die "Unable to reopen STDOUT\n";
+
+# Set unbuffered output
+$| = 1;
+
+while ($ARGV [0]) {
+  if ($ARGV [0] eq "-D") {
+    $daemon_mode = 1;
+  } elsif ($ARGV [0] eq "-v") {
+    $verbose = 1;
+    undef ($quiet_mode);
+  } elsif ($ARGV [0] eq "-d") {
+    $debug = 1;
+    undef ($quiet_mode);
+  } elsif ($ARGV [0] eq "-m") {
+    $multithreaded = 1;
+  } elsif ($ARGV [0] eq "-q") {
+    $quiet_mode = 1;
+    undef ($verbose);
+  } elsif ($ARGV [0] eq "-p") {
+    shift @ARGV;
+    Usage "Must specify a port # after -p" if (!defined $ARGV [0]);
+    $port = $ARGV[0];
+  } else {
+    Usage "Unknown parameter found: " . $ARGV[0];
+  } # if
+
+  shift @ARGV;
+} # while
+
+my $tmp = (!defined $ENV {TMP}) ? "/tmp" : $ENV {TMP};
+my $ecrd_logfile = "$tmp/$me.log";
+my $ecrd_errfile = "$tmp/$me.err";
+
+EnterDaemonMode ($ecrd_logfile, $ecrd_logfile) if defined ($daemon_mode);
+
+OpenDB;
+
+# Set up signal handlers
+$SIG{ALRM} = \&timeout;
+$SIG{INT}  = $SIG{QUIT} = \&interrupt;
+
+ProcessRequests;
diff --git a/clients/LynuxWorks/bin/ecrdesc b/clients/LynuxWorks/bin/ecrdesc
new file mode 100644
index 0000000..9c70fb1
--- /dev/null
+++ b/clients/LynuxWorks/bin/ecrdesc
@@ -0,0 +1,65 @@
+#!/usr/bin/perl
+################################################################################
+#
+# File:         ecrdesc
+# Description:  This script will dump out the description for the ECR #(s) 
+#		passed in.
+# Author:       Andrew@DeFaria.com
+# Created:      Fri Jan  7 15:35:13 PST 2005
+# Language:     Perl
+#
+# (c) Copyright 2005, LynxWorks Inc., all rights reserved
+#
+################################################################################
+use strict;
+use warnings;
+use DBI;
+
+my $DB;
+
+# Called when a database error has occurred
+sub DBError {
+  my $msg       = shift;
+  my $statement = shift;
+
+  print $msg . "\nError #" . $DB->err . " " . $DB->errstr . "\n";
+
+  if (defined $statement) {
+    print "SQL Statement: $statement\n";
+  } # if
+
+  exit $DB->err;
+} # DBError
+
+# Connect to database. Note this is using anonymous access (read only)
+$DB = DBI->connect("DBI:Informix:lynxmigr1")
+  or DBError "Unable to open database";
+
+# Loop through ECR #s from the command line
+foreach my $ecr (@ARGV) {
+  print "ECR #: $ecr\n";
+
+  my $statement	= "select description from defect where pkey=\"$ecr\"";
+  my $sth	= $DB->prepare ($statement)
+    or DBError "Unable to prepare statement", $statement;
+
+  $sth->execute ()
+    or DBError "Unable to execute statement", $statement;
+
+  # Defect records are unique per pkey (AKA ECR) there for there will
+  # only be one entry in @row. Also the description is returned as one
+  # large string.
+  my @row = $sth->fetchrow_array;
+
+  if (!@row) {
+    # @row is empty if there was no ECR by that number
+    print "Nothing found!\n";
+  } else {
+    my $desc = pop @row;
+    print "Description:\n" . "-" x 80 . "\n" . $desc . "\n" . "-" x 80 . "\n";
+  } # if
+} # foreach
+
+$DB->disconnect;
+
+exit;
diff --git a/clients/LynuxWorks/bin/files4cr b/clients/LynuxWorks/bin/files4cr
new file mode 100644
index 0000000..5085dce
--- /dev/null
+++ b/clients/LynuxWorks/bin/files4cr
@@ -0,0 +1,202 @@
+#!/usr/bin/perl
+################################################################################
+#
+# File:         files4cr
+# Description:  This script will go through CVS looking for files that have
+#		the passed in CR #.
+# Author:       Andrew@DeFaria.com
+# Created:      Fri Dec 17 12:18:21 PST 2004
+# Language:     Perl
+#
+# (c) Copyright 2004, LynxWorks Inc., all rights reserved
+#
+################################################################################
+use warnings;
+use strict;
+
+# Options
+my $verbose	= 0;
+my $debug	= 0;
+my $execute	= 0;
+my $local	= "";
+
+my $cr;
+
+sub verbose {
+  my $msg = shift;
+
+  print "$msg\n" if $verbose;
+} # verbose
+
+sub debug {
+  my $msg = shift;
+
+  print "DEBUG: $msg\n" if $debug;
+} # debug
+
+sub Usage {
+  my $msg = shift;
+
+  print "ERROR: $msg\n\n" if defined $msg;
+
+  print "Usage: files4cr [-v] [-d] [-l] [-x] [-u] \n";
+  print "\nWhere:\n\n";
+  print "\t-v:\t\tTurn on verbose mode (Default: off)\n";
+  print "\t-d:\t\tTurn on debug mode (Default: off)\n";
+  print "\t-l:\t\tLocal directory only, no recursion\n";
+  print "\t-x:\t\tTurn on execute mode (Default: off)\n";
+  print "\t-u:\t\tDisplay usage\n";
+  print "\tcr\t\tCR number to search for\n";
+  exit 1;
+} # Usage
+
+sub GetFiles4 {
+  my $cr	= shift;
+  my $local	= shift;
+
+  # Perform a cvs log command and grep through the output
+  print "Gathering CVS info..." if $verbose;
+  my @output = grep {
+    /^Working file: /	or
+    /^revision /	or
+    /^date: /		or
+    /^\s*CR#/		or
+    /^\s*CR /
+  } `cvs -q log $local 2>/dev/null`;
+  verbose " done";
+
+  # Now process this array. Entries may look like:
+  #
+  # Working file: 
+  # revision 
+  # date:...
+  # revision 
+  # date:...
+  # CR Number: 
+  #
+  # It's quite possible that there are no CR numbers for a file. It's also
+  # possible that there is the same CR number for multiple revisions! For
+  # example:
+  #
+  # Working file: 
+  # revision 10.2
+  # date:...
+  # CR Number: 1000
+  # revision 10.1
+  # date:...
+  # CR Number: 1000
+  #
+  # In this case we want to return the  and 10.2.
+
+  my %files;
+  my $filename;
+  my $revision;
+
+  while ($_ = shift @output) {
+    chomp;
+    chop if /\r/;
+
+    if (/^Working file: (.*)/) {
+      $filename = $1;
+      debug "file: $filename";
+    } elsif (/^revision (.*)/) {
+      $revision = $1;
+      debug "revision: $revision";
+    } elsif (/^date:.*state: (.*);.*/) {
+      # Check to see if dead!
+      if ($1 eq "dead") {
+	# Indicate we're dead by setting $revision to blank.
+	debug "Dead file encountered $filename";
+	$revision= "";
+      } # if
+    } elsif (/^CR Number: (\d*)$/	or
+	     /^CR# (\d*)$/		or
+             /^CR # (\d*)$/	 	or
+             /^\s*CR (\d*)/) {
+      debug "CR: $1";
+      if ($cr eq $1) {
+	$files{$filename} = $revision;
+	debug "Set $filename: $revision";
+
+        # Now skip to next file
+	do {
+	  $_ = shift @output;
+	} while @output and !/^Working file: /;
+	unshift @output, $_;
+      } # if
+    } else {
+      verbose "Unknown line encountered: $_\n";
+    } # if
+  } # foreach
+
+  return %files;
+} # GetFiles4
+
+sub GetWorkingRev {
+  my $filename = shift;
+
+  my @output = grep { /Working revision:/ } `cvs status $filename`;
+
+  if (defined $output [0] and $output [0] =~ /Working revision:\s*(\S*)/) {
+    return $1;
+  } # if
+
+  return undef;
+} # GetWorkingRev
+
+# Get args
+while ($ARGV [0]) {
+  if ($ARGV [0] eq "-d") {
+    $debug = 1;
+  } elsif ($ARGV [0] eq "-v") {
+    $verbose = 1;
+  } elsif ($ARGV [0] eq "-l") {
+    $local = $ARGV [0];
+  } elsif ($ARGV [0] eq "-x") {
+    $execute = 1;
+  } elsif ($ARGV [0] eq "-u") {
+    Usage;
+  } # if
+
+  $cr = $ARGV [0];
+
+  shift (@ARGV);
+} # while
+
+Usage "No CR specified to process" if !defined $cr;
+
+my %files = GetFiles4 $cr, $local;
+
+foreach (keys %files) {
+  if ($files{$_} eq "") {
+    print "$_: Is dead\n";
+    next;
+  } # if
+
+  my $working_revision	= GetWorkingRev $_;
+  my $up_to_date	= 0;
+
+  if (defined $working_revision and $working_revision eq $files{$_}) {
+    $up_to_date = 1;
+  } # if
+
+  if ($execute) {
+    print "cvs update -r$files{$_}  $_";
+
+    if (!$up_to_date) {
+      `cvs update -r$files{$_} $_`;
+      print " - Updated\n";
+    } else {
+      print " - Already up to date\n";
+    } # if
+  } else {
+    print "$_: $files{$_}";
+
+    if ($up_to_date) {
+      print " - Already up to date\n";
+    } else {
+      print " - Out of date\n";
+    } # if
+  } # if
+} # foreach
+
diff --git a/clients/LynuxWorks/bin/files4ecr b/clients/LynuxWorks/bin/files4ecr
new file mode 100644
index 0000000..31c381a
--- /dev/null
+++ b/clients/LynuxWorks/bin/files4ecr
@@ -0,0 +1,185 @@
+#!/usr/bin/perl
+################################################################################
+#
+# File:         files4ecr
+# Description:  This script will go through CVS looking for files that have
+#		the passed in ECR #.
+# Author:       Andrew@DeFaria.com
+# Created:      Fri Dec 17 12:18:21 PST 2004
+# Language:     Perl
+#
+# (c) Copyright 2004, LynxWorks Inc., all rights reserved
+#
+################################################################################
+use warnings;
+use strict;
+
+# Options
+my $verbose	= 0;
+my $debug	= 0;
+my $execute	= 0;
+my $local	= "";
+
+my $ecr;
+
+sub verbose {
+  my $msg = shift;
+
+  print "$msg\n" if $verbose;
+} # verbose
+
+sub debug {
+  my $msg = shift;
+
+  print "DEBUG: $msg\n" if $debug;
+} # debug
+
+sub Usage {
+  my $msg = shift;
+
+  print "ERROR: $msg\n\n" if defined $msg;
+
+  print "Usage: files4ecr [-v] [-d] [-l] [-x] [-u] \n";
+  print "\nWhere:\n\n";
+  print "\t-v:\t\tTurn on verbose mode (Default: off)\n";
+  print "\t-d:\t\tTurn on debug mode (Default: off)\n";
+  print "\t-l:\t\tLocal directory only, no recursion\n";
+  print "\t-x:\t\tTurn on execute mode (Default: off)\n";
+  print "\t-u:\t\tDisplay usage\n";
+  print "\tecr\t\tECR number to search for\n";
+  exit 1;
+} # Usage
+
+sub GetFiles4 {
+  my $ecr	= shift;
+  my $local	= shift;
+
+  # Perform a cvs log command and grep through the output
+  print "Gathering CVS info..." if $verbose;
+  my @output = grep {
+    /^Working file: /	or
+    /^revision /	or
+    /^\s*ECR#/		or
+    /^\s*ECR /
+  } `cvs -q log $local 2>/dev/null`;
+  verbose " done";
+
+  # Now process this array. Entries may look like:
+  #
+  # Working file: 
+  # revision 
+  # revision 
+  # ECR Number: 
+  #
+  # It's quite possible that there are no ECR numbers for a file. It's also
+  # possible that there is the same ECR number for multiple revisions! For
+  # example:
+  #
+  # Working file: 
+  # revision 10.2
+  # ECR Number: 1000
+  # revision 10.1
+  # ECR Number: 1000
+  #
+  # In this case we want to return the  and 10.2.
+
+  my %files;
+  my $filename;
+  my $revision;
+
+  while ($_ = shift @output) {
+    chomp;
+    chop if /\r/;
+
+    if (/^Working file: (.*)/) {
+      $filename = $1;
+      debug "file: $filename";
+    } elsif (/^revision (.*)/) {
+      $revision = $1;
+      debug "revision: $revision";
+    } elsif (/^ECR Number: (\d*)$/	or
+	     /^ECR# (\d*)$/		or
+             /^ECR # (\d*)$/	 	or
+             /^\s*ECR (\d*)/) {
+      debug "ECR: $1";
+      if ($ecr eq $1) {
+	$files{$filename} = $revision;
+	debug "Set $filename: $revision";
+
+        # Now skip to next file
+	do {
+	  $_ = shift @output;
+	} while @output and !/Working file: /;
+	unshift @output, $_;
+      } # if
+    } else {
+      verbose "Unknown line encountered: $_\n";
+    } # if
+  } # foreach
+
+  return %files;
+} # GetFiles4
+
+sub GetWorkingRev {
+  my $filename = shift;
+
+  my @output = grep { /Working revision:/ } `cvs status $filename`;
+
+  if (defined $output [0] and $output [0] =~ /Working revision:\s*(\S*)/) {
+    return $1;
+  } # if
+
+  return undef;
+} # GetWorkingRev
+
+# Get args
+while ($ARGV [0]) {
+  if ($ARGV [0] eq "-d") {
+    $debug = 1;
+  } elsif ($ARGV [0] eq "-v") {
+    $verbose = 1;
+  } elsif ($ARGV [0] eq "-l") {
+    $local = $ARGV [0];
+  } elsif ($ARGV [0] eq "-x") {
+    $execute = 1;
+  } elsif ($ARGV [0] eq "-u") {
+    Usage;
+  } # if
+
+  $ecr = $ARGV [0];
+
+  shift (@ARGV);
+} # while
+
+Usage "No ECR specified to process" if !defined $ecr;
+
+my %files = GetFiles4 $ecr, $local;
+
+foreach (keys %files) {
+  my $working_revision	= GetWorkingRev $_;
+  my $up_to_date	= 0;
+
+  if (defined $working_revision and $working_revision eq $files{$_}) {
+    $up_to_date = 1;
+  } # if
+
+  if ($execute) {
+    print "cvs update -r$files{$_}  $_";
+
+    if (!$up_to_date) {
+      `cvs update -r$files{$_} $_`;
+      print " - Updated\n";
+    } else {
+      print " - Already up to date\n";
+    } # if
+  } else {
+    print "$_: $files{$_}";
+
+    if ($up_to_date) {
+      print " - Already up to date\n";
+    } else {
+      print " - Out of date\n";
+    } # if
+  } # if
+} # foreach
+
diff --git a/clients/LynuxWorks/bin/files4tag b/clients/LynuxWorks/bin/files4tag
new file mode 100644
index 0000000..7a420ec
--- /dev/null
+++ b/clients/LynuxWorks/bin/files4tag
@@ -0,0 +1,17 @@
+#!/bin/bash
+tag=$1
+file=""
+revision=""
+cvs -q log 2>/dev/null | grep -E "(Working file: |$tag)" | grep -B1 $tag |
+  while read line; do
+    if [[ $line == Working\ file:\ * ]]; then
+      file=$(echo $line | sed "s/Working file: //")
+    elif [[ $line == $tag* ]]; then
+      revision=$(echo $line | sed "s/$tag: //")
+    fi
+    if [ "$file" != "" -a "$revision" != "" ]; then
+      echo cvs update -r$revision $file
+      file=""
+      revision=""
+    fi
+  done
diff --git a/clients/LynuxWorks/lib/Diff.pm b/clients/LynuxWorks/lib/Diff.pm
new file mode 100644
index 0000000..98b7611
--- /dev/null
+++ b/clients/LynuxWorks/lib/Diff.pm
@@ -0,0 +1,584 @@
+package Diff;
+use strict;
+use vars qw($VERSION @EXPORT_OK @ISA @EXPORT);
+use integer;		# see below in _replaceNextLargerWith() for mod to make
+					# if you don't use this
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw();
+@EXPORT_OK = qw(LCS diff traverse_sequences);
+$VERSION = sprintf('%d.%02d', (q$Revision: 1.10 $ =~ /\d+/g));
+
+# McIlroy-Hunt diff algorithm
+# Adapted from the Smalltalk code of Mario I. Wolczko, 
+# by Ned Konz, perl@bike-nomad.com
+
+=head1 NAME
+
+Algorithm::Diff - Compute `intelligent' differences between two files / lists
+
+=head1 SYNOPSIS
+
+  use Algorithm::Diff qw(diff LCS traverse_sequences);
+
+  @lcs    = LCS( \@seq1, \@seq2 );
+
+  @lcs    = LCS( \@seq1, \@seq2, $key_generation_function );
+
+  $lcsref = LCS( \@seq1, \@seq2 );
+
+  $lcsref = LCS( \@seq1, \@seq2, $key_generation_function );
+
+  @diffs = diff( \@seq1, \@seq2 );
+
+  @diffs = diff( \@seq1, \@seq2, $key_generation_function );
+  
+  traverse_sequences( \@seq1, \@seq2,
+                     { MATCH => $callback,
+                       DISCARD_A => $callback,
+                       DISCARD_B => $callback,
+                     } );
+
+  traverse_sequences( \@seq1, \@seq2,
+                     { MATCH => $callback,
+                       DISCARD_A => $callback,
+                       DISCARD_B => $callback,
+                     },
+                     $key_generation_function );
+
+=head1 INTRODUCTION
+
+(by Mark-Jason Dominus)
+
+I once read an article written by the authors of C; they said
+that they hard worked very hard on the algorithm until they found the
+right one.
+
+I think what they ended up using (and I hope someone will correct me,
+because I am not very confident about this) was the `longest common
+subsequence' method.  in the LCS problem, you have two sequences of
+items:
+
+        a b c d f g h j q z
+
+        a b c d e f g i j k r x y z
+
+and you want to find the longest sequence of items that is present in
+both original sequences in the same order.  That is, you want to find
+a new sequence I which can be obtained from the first sequence by
+deleting some items, and from the secend sequence by deleting other
+items.  You also want I to be as long as possible.  In this case
+I is
+
+        a b c d f g j z
+
+From there it's only a small step to get diff-like output:
+
+        e   h i   k   q r x y 
+        +   - +   +   - + + +
+
+This module solves the LCS problem.  It also includes a canned
+function to generate C-like output.
+
+It might seem from the example above that the LCS of two sequences is
+always pretty obvious, but that's not always the case, especially when
+the two sequences have many repeated elements.  For example, consider
+
+	a x b y c z p d q
+	a b c a x b y c z
+
+A naive approach might start by matching up the C and C that
+appear at the beginning of each sequence, like this:
+
+	a x b y c         z p d q
+	a   b   c a b y c z
+
+This finds the common subsequence C.  But actually, the LCS
+is C:
+
+	      a x b y c z p d q
+	a b c a x b y c z
+
+=head1 USAGE
+
+This module provides three exportable functions, which we'll deal with in
+ascending order of difficulty: C, C, and
+C.
+
+=head2 C
+
+Given references to two lists of items, LCS returns an array containing their
+longest common subsequence.  In scalar context, it returns a reference to
+such a list.
+
+  @lcs    = LCS( \@seq1, \@seq2 );
+  $lcsref = LCS( \@seq1, \@seq2 );
+
+C may be passed an optional third parameter; this is a CODE
+reference to a key generation function.  See L.
+
+  @lcs    = LCS( \@seq1, \@seq2, $keyGen );
+  $lcsref = LCS( \@seq1, \@seq2, $keyGen );
+
+Additional parameters, if any, will be passed to the key generation
+routine.
+
+=head2 C
+
+  @diffs     = diff( \@seq1, \@seq2 );
+  $diffs_ref = diff( \@seq1, \@seq2 );
+
+C computes the smallest set of additions and deletions necessary
+to turn the first sequence into the second, and returns a description
+of these changes.  The description is a list of I; each hunk
+represents a contiguous section of items which should be added,
+deleted, or replaced.  The return value of C is a list of
+hunks, or, in scalar context, a reference to such a list.
+
+Here is an example:  The diff of the following two sequences:
+
+  a b c e h j l m n p
+  b c d e f j k l m r s t
+
+Result:
+
+ [ 
+   [ [ '-', 0, 'a' ] ],       
+
+   [ [ '+', 2, 'd' ] ],
+
+   [ [ '-', 4, 'h' ] , 
+     [ '+', 4, 'f' ] ],
+
+   [ [ '+', 6, 'k' ] ],
+
+   [ [ '-', 8, 'n' ], 
+     [ '-', 9, 'p' ], 
+     [ '+', 9, 'r' ], 
+     [ '+', 10, 's' ], 
+     [ '+', 11, 't' ],
+   ]
+ ]
+
+There are five hunks here.  The first hunk says that the C at
+position 0 of the first sequence should be deleted (C<->).  The second
+hunk says that the C at position 2 of the second sequence should
+be inserted (C<+>).  The third hunk says that the C at position 4
+of the first sequence should be removed and replaced with the C
+from position 4 of the second sequence.  The other two hunks similarly. 
+
+C may be passed an optional third parameter; this is a CODE
+reference to a key generation function.  See L.
+
+Additional parameters, if any, will be passed to the key generation
+routine.
+
+=head2 C
+
+C is the most general facility provided by this
+module; C and C are implemented as calls to it.
+
+Imagine that there are two arrows.  Arrow A points to an element of
+sequence A, and arrow B points to an element of the sequence B.
+Initially, the arrows point to the first elements of the respective
+sequences.  C will advance the arrows through the
+sequences one element at a time, calling an appropriate user-specified
+callback function before each advance.  It willadvance the arrows in
+such a way that if there are equal elements C<$A[$i]> and C<$B[$j]>
+which are equal and which are part of the LCS, there will be some
+moment during the execution of C when arrow A is
+pointing to C<$A[$i]> and arrow B is pointing to C<$B[$j]>.  When this
+happens, C will call the C callback
+function and then it will advance both arrows. 
+
+Otherwise, one of the arrows is pointing to an element of its sequence
+that is not part of the LCS.  C will advance that
+arrow and will call the C or the C callback,
+depending on which arrow it advanced.  If both arrows point to
+elements that are not part of the LCS, then C will
+advance one of them and call the appropriate callback, but it is not
+specified which it will call.
+
+The arguments to C are the two sequences to
+traverse, and a callback which specifies the callback functions, like
+this:
+
+  traverse_sequences( \@seq1, \@seq2,
+                     { MATCH => $callback_1,
+                       DISCARD_A => $callback_2,
+                       DISCARD_B => $callback_3,
+                     } );
+
+Callbacks are invoked with at least the indices of the two arrows as
+their arguments.  They are not expected to return any values.  If a
+callback is omitted from the table, it is not called.
+
+If arrow A reaches the end of its sequence, before arrow B does,
+C will call the C callback when it
+advances arrow B, if there is such a function; if not it will call
+C instead.  Similarly if arrow B finishes first.
+C returns when both arrows are at the ends of
+their respective sequences.  It returns true on success and false on
+failure.  At present there is no way to fail.
+
+C may be passed an optional fourth parameter; this
+is a CODE reference to a key generation function.  See L.
+
+Additional parameters, if any, will be passed to the key generation
+function.
+
+=head1 KEY GENERATION FUNCTIONS
+
+C, C, and C accept an optional last parameter.
+This is a CODE reference to a key generating (hashing) function that should
+return a string that uniquely identifies a given element.
+It should be the case that if two elements are to be considered equal,
+their keys should be the same (and the other way around).
+If no key generation function is provided, the key will be the
+element as a string.
+
+By default, comparisons will use "eq" and elements will be turned into keys
+using the default stringizing operator '""'.
+
+Where this is important is when you're comparing something other than
+strings. If it is the case that you have multiple different objects 
+that should be considered to be equal, you should supply a key
+generation function. Otherwise, you have to make sure that your arrays
+contain unique references.
+
+For instance, consider this example:
+
+  package Person;
+
+  sub new
+  {
+    my $package = shift;
+    return bless { name => '', ssn => '', @_ }, $package;
+  }
+
+  sub clone
+  {
+    my $old = shift;
+    my $new = bless { %$old }, ref($old);
+  }
+
+  sub hash
+  {
+    return shift()->{'ssn'};
+  }
+
+  my $person1 = Person->new( name => 'Joe', ssn => '123-45-6789' );
+  my $person2 = Person->new( name => 'Mary', ssn => '123-47-0000' );
+  my $person3 = Person->new( name => 'Pete', ssn => '999-45-2222' );
+  my $person4 = Person->new( name => 'Peggy', ssn => '123-45-9999' );
+  my $person5 = Person->new( name => 'Frank', ssn => '000-45-9999' );
+
+If you did this:
+
+  my $array1 = [ $person1, $person2, $person4 ];
+  my $array2 = [ $person1, $person3, $person4, $person5 ];
+  Algorithm::Diff::diff( $array1, $array2 );
+
+everything would work out OK (each of the objects would be converted
+into a string like "Person=HASH(0x82425b0)" for comparison).
+
+But if you did this:
+
+  my $array1 = [ $person1, $person2, $person4 ];
+  my $array2 = [ $person1, $person3, $person4->clone(), $person5 ];
+  Algorithm::Diff::diff( $array1, $array2 );
+
+$person4 and $person4->clone() (which have the same name and SSN)
+would be seen as different objects. If you wanted them to be considered
+equivalent, you would have to pass in a key generation function:
+
+  my $array1 = [ $person1, $person2, $person4 ];
+  my $array2 = [ $person1, $person3, $person4->clone(), $person5 ];
+  Algorithm::Diff::diff( $array1, $array2, \&Person::hash );
+
+This would use the 'ssn' field in each Person as a comparison key, and
+so would consider $person4 and $person4->clone() as equal.
+
+You may also pass additional parameters to the key generation function
+if you wish.
+
+=head1 AUTHOR
+
+This version by Ned Konz, perl@bike-nomad.com
+
+=head1 CREDITS
+
+Versions through 0.59 (and much of this documentation) were written by:
+
+Mark-Jason Dominus, mjd-perl-diff@plover.com
+
+This version borrows the documentation and names of the routines
+from Mark-Jason's, but has all new code in Diff.pm.
+
+This code was adapted from the Smalltalk code of
+Mario Wolczko , which is available at
+ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st
+
+The algorithm is that described in 
+I,
+CACM, vol.20, no.5, pp.350-353, May 1977, with a few
+minor improvements to improve the speed.
+
+=cut
+
+# Create a hash that maps each element of $aCollection to the set of positions
+# it occupies in $aCollection, restricted to the elements within the range of
+# indexes specified by $start and $end.
+# The fourth parameter is a subroutine reference that will be called to
+# generate a string to use as a key.
+# Additional parameters, if any, will be passed to this subroutine.
+#
+# my $hashRef = _withPositionsOfInInterval( \@array, $start, $end, $keyGen );
+
+sub _withPositionsOfInInterval
+{
+	my $aCollection = shift;	# array ref
+	my $start = shift;
+	my $end = shift;
+	my $keyGen = shift;
+	my %d;
+	my $index;
+	for ( $index = $start; $index <= $end; $index++ )
+	{
+		my $element = $aCollection->[ $index ];
+		my $key = &$keyGen( $element, @_ );
+		if ( exists( $d{ $key } ) )
+		{
+			push( @{ $d{ $key } }, $index );
+		}
+		else
+		{
+			$d{ $key } = [ $index ];
+		}
+	}
+	return wantarray ? %d: \%d;
+}
+
+# Find the place at which aValue would normally be inserted into the array. If
+# that place is already occupied by aValue, do nothing, and return undef. If
+# the place does not exist (i.e., it is off the end of the array), add it to
+# the end, otherwise replace the element at that point with aValue.
+# It is assumed that the array's values are numeric.
+# This is where the bulk (75%) of the time is spent in this module, so try to
+# make it fast!
+
+sub _replaceNextLargerWith
+{
+	my ( $array, $aValue, $high ) = @_;
+	$high ||= $#$array;
+
+	# off the end?
+	if ( $high == -1 || $aValue > $array->[ -1 ] )
+	{
+		push( @$array, $aValue );
+		return $high + 1;
+	}
+
+	# binary search for insertion point...
+	my $low = 0;
+	my $index;
+	my $found;
+	while ( $low <= $high )
+	{
+		$index = ( $high + $low ) / 2;
+#		$index = int(( $high + $low ) / 2);		# without 'use integer'
+		$found = $array->[ $index ];
+
+		if ( $aValue == $found )
+		{
+			return undef;
+		}
+		elsif ( $aValue > $found )
+		{
+			$low = $index + 1;
+		}
+		else
+		{
+			$high = $index - 1;
+		}
+	}
+
+	# now insertion point is in $low.
+	$array->[ $low ] = $aValue;		# overwrite next larger
+	return $low;
+}
+
+# This method computes the longest common subsequence in $a and $b.
+
+# Result is array or ref, whose contents is such that
+# 	$a->[ $i ] = $b->[ $result[ $i ] ]
+# foreach $i in ( 0..scalar( @result ) if $result[ $i ] is defined.
+
+# An additional argument may be passed; this is a hash or key generating
+# function that should return a string that uniquely identifies the given
+# element.  It should be the case that if the key is the same, the elements
+# will compare the same. If this parameter is undef or missing, the key
+# will be the element as a string.
+
+# By default, comparisons will use "eq" and elements will be turned into keys
+# using the default stringizing operator '""'.
+
+# Additional parameters, if any, will be passed to the key generation routine.
+
+sub _longestCommonSubsequence
+{
+	my $a = shift;	# array ref
+	my $b = shift;	# array ref
+	my $keyGen = shift;	# code ref
+	my $compare;	# code ref
+
+	# set up code refs
+	# Note that these are optimized.
+	if ( !defined( $keyGen ) )	# optimize for strings
+	{
+		$keyGen = sub { $_[0] };
+		$compare = sub { my ($a, $b) = @_; $a eq $b };
+	}
+	else
+	{
+		$compare = sub {
+			my $a = shift; my $b = shift;
+			&$keyGen( $a, @_ ) eq &$keyGen( $b, @_ )
+		};
+	}
+
+	my ($aStart, $aFinish, $bStart, $bFinish, $matchVector) = (0, $#$a, 0, $#$b, []);
+
+	# First we prune off any common elements at the beginning
+	while ( $aStart <= $aFinish
+		and $bStart <= $bFinish
+		and &$compare( $a->[ $aStart ], $b->[ $bStart ], @_ ) )
+	{
+		$matchVector->[ $aStart++ ] = $bStart++;
+	}
+
+	# now the end
+	while ( $aStart <= $aFinish
+		and $bStart <= $bFinish
+		and &$compare( $a->[ $aFinish ], $b->[ $bFinish ], @_ ) )
+	{
+		$matchVector->[ $aFinish-- ] = $bFinish--;
+	}
+
+	# Now compute the equivalence classes of positions of elements
+	my $bMatches = _withPositionsOfInInterval( $b, $bStart, $bFinish, $keyGen, @_ );
+	my $thresh = [];
+	my $links = [];
+
+	my ( $i, $ai, $j, $k );
+	for ( $i = $aStart; $i <= $aFinish; $i++ )
+	{
+		$ai = &$keyGen( $a->[ $i ] );
+		if ( exists( $bMatches->{ $ai } ) )
+		{
+			$k = 0;
+			for $j ( reverse( @{ $bMatches->{ $ai } } ) )
+			{
+				# optimization: most of the time this will be true
+				if ( $k
+					and $thresh->[ $k ] > $j
+					and $thresh->[ $k - 1 ] < $j )
+				{
+					$thresh->[ $k ] = $j;
+				}
+				else
+				{
+					$k = _replaceNextLargerWith( $thresh, $j, $k );
+				}
+
+				# oddly, it's faster to always test this (CPU cache?).
+				if ( defined( $k ) )
+				{
+					$links->[ $k ] = 
+						[ ( $k ? $links->[ $k - 1 ] : undef ), $i, $j ];
+				}
+			}
+		}
+	}
+
+	if ( @$thresh )
+	{
+		for ( my $link = $links->[ $#$thresh ]; $link; $link = $link->[ 0 ] )
+		{
+			$matchVector->[ $link->[ 1 ] ] = $link->[ 2 ];
+		}
+	}
+
+	return wantarray ? @$matchVector : $matchVector;
+}
+
+sub traverse_sequences
+{
+	my $a = shift;	# array ref
+	my $b = shift;	# array ref
+	my $callbacks = shift || { };
+	my $keyGen = shift;
+	my $matchCallback = $callbacks->{'MATCH'} || sub { };
+	my $discardACallback = $callbacks->{'DISCARD_A'} || sub { };
+	my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { };
+	my $matchVector = _longestCommonSubsequence( $a, $b, $keyGen, @_ );
+	# Process all the lines in match vector
+	my $lastA = $#$a;
+	my $lastB = $#$b;
+	my $bi = 0;
+	my $ai;
+	for ( $ai = 0; $ai <= $#$matchVector; $ai++ )
+	{
+		my $bLine = $matchVector->[ $ai ];
+		if ( defined( $bLine ) )
+		{
+			&$discardBCallback( $ai, $bi++, @_ ) while $bi < $bLine;
+			&$matchCallback( $ai, $bi++, @_ );
+		}
+		else
+		{
+			&$discardACallback( $ai, $bi, @_ );
+		}
+	}
+
+	&$discardACallback( $ai++, $bi, @_ ) while ( $ai <= $lastA );
+	&$discardBCallback( $ai, $bi++, @_ ) while ( $bi <= $lastB );
+	return 1;
+}
+
+sub LCS
+{
+	my $a = shift;	# array ref
+	my $matchVector = _longestCommonSubsequence( $a, @_ );
+	my @retval;
+	my $i;
+	for ( $i = 0; $i <= $#$matchVector; $i++ )
+	{
+		if ( defined( $matchVector->[ $i ] ) )
+		{
+			push( @retval, $a->[ $i ] );
+		}
+	}
+	return wantarray ? @retval : \@retval;
+}
+
+sub diff
+{
+	my $a = shift;	# array ref
+	my $b = shift;	# array ref
+	my $retval = [];
+	my $hunk = [];
+	my $discard = sub { push( @$hunk, [ '-', $_[ 0 ], $a->[ $_[ 0 ] ] ] ) };
+	my $add = sub { push( @$hunk, [ '+', $_[ 1 ], $b->[ $_[ 1 ] ] ] ) };
+	my $match = sub { push( @$retval, $hunk ) if scalar(@$hunk); $hunk = [] };
+	traverse_sequences( $a, $b,
+		{ MATCH => $match, DISCARD_A => $discard, DISCARD_B => $add },
+		@_ );
+	&$match();
+	return wantarray ? @$retval : $retval;
+}
+
+1;
diff --git a/clients/LynuxWorks/lib/ecrc.pm b/clients/LynuxWorks/lib/ecrc.pm
new file mode 100644
index 0000000..456edc3
--- /dev/null
+++ b/clients/LynuxWorks/lib/ecrc.pm
@@ -0,0 +1,220 @@
+#!/usr/bin/perl
+################################################################################
+#
+# File:		ecrd.pm: ECR Daemon Client Library
+# Description:  Perl Module interface to ecrd (ECR Daemon). This is used
+#		by ecrc and cgi scripts to talk to ECR Daemon
+# Author:       Andrew@DeFaria.com
+# Created:      Tue Feb 15 09:40:57 PST 2005
+# Modified:
+# Language:     Perl
+#
+# (c) Copyright 2005, LynuxWorks, all rights reserved.
+#
+################################################################################
+use strict;
+use warnings;
+
+use IO::Socket;
+
+package ecrc;
+  require Exporter;
+  @main::ISA = qw (Exporter);
+
+  @main::EXPORT = qw (Connect GetECRRecord Disconnect);
+
+  my $default_server	= (!defined $ENV {ECRDSERVER}) ? "lynx12" : $ENV {ECRDSERVER};
+  my $default_port	= (!defined $ENV {ECRDPORT})   ? 1500     : $ENV {ECRDPORT};
+  my $verbose		= 0;
+  my $debug		= 0;
+  my $command;
+  my $ecrserver;
+
+  # Forwards
+  sub ConnectToServer;
+  sub GetServerAck;
+  sub GetServerList;
+  sub GetServerResponse;
+  sub SendServerAck;
+  sub SendServerCmd;
+
+  BEGIN {
+    my $ecrcversion = "1.1";
+
+    # Reopen STDOUT to make sure it's clear
+    open STDOUT, ">-" or die "Unable to reopen STDOUT\n";
+
+    # Set unbuffered output
+    $| = 1;
+  } # BEGIN
+
+  sub set_verbose {
+    $verbose = 1;
+  } # set_verbose
+
+  sub set_debug {
+    $debug = 1;
+  } # set_debug
+
+  sub verbose {
+    print "@_\n" if $verbose;
+  } # verbose
+
+  sub debug {
+    print "DEBUG: @_\n" if $debug;
+  } # debug
+
+  sub Connect {
+    my $host = shift;
+    my $port = shift;
+
+    my $result;
+
+    $host = $default_server if !defined $host;
+    $port = $default_port   if !defined $port;
+
+    $ecrserver = ConnectToServer $host, $port;
+
+    if ($ecrserver) {
+      verbose "Connected to $host";
+      SendServerAck $ecrserver;
+    } # if
+
+    return $ecrserver;
+  } # Connect
+
+  sub Disconnect {
+    my $msg;
+
+    if ($ecrserver) {
+      if ($command eq "shutdown") {
+	$msg = "Disconnected from server - shutdown server";
+      } else {
+	$command = "quit";
+	$msg     = "Disconnected from server";
+      } # if
+      SendServerCmd $ecrserver, $command;
+      GetServerAck  $ecrserver;
+      verbose "$msg";
+      close $ecrserver;
+      undef $ecrserver;
+    } # if
+  } # Disconnect
+
+  sub GetECRRecord {
+    my $ecr = shift;
+
+    my %fields;
+    my @ecrs;
+
+    if (!$ecrserver) {
+      verbose "Not connected to server yet!";
+      verbose "Attempting connection to $default_server...";
+      if (!Connect $default_server, $default_port) {
+	print "Unable to connect to server $default_server\n";
+	exit 1;
+      } # if
+    } # if
+
+    SendServerCmd $ecrserver, $ecr;
+    GetServerAck  $ecrserver;
+
+    if ($ecr eq "\*") {
+      @ecrs = GetServerList $ecrserver;
+    } else {
+      %fields = GetServerResponse $ecrserver;
+    } # if
+
+   SendServerAck $ecrserver;
+
+    return $ecr eq "\*" ? @ecrs : %fields;
+  } # GetECRRecord
+
+  END {
+    verbose "Sending disconnect command to server";
+    $command = "quit";
+    Disconnect;
+  } # END
+
+  sub ConnectToServer {
+    my $host = shift;
+    my $port = shift;
+
+    # create a tcp connection to the specified host and port
+    return IO::Socket::INET->new(Proto     => "tcp",
+				 PeerAddr  => $host,
+				 PeerPort  => $port);
+  } # ConnectToServer
+
+  sub SendServerAck {
+    my $server = shift;
+
+    print $server "ACK\n";
+  } # SendServerAck
+
+  sub GetServerAck {
+    my $server = shift;
+    my $srvresp;
+
+    while (defined ($srvresp = <$server>)) {
+      chomp $srvresp;
+      if ($srvresp eq "ACK") {
+	return;
+      } # if
+      print "Received $srvresp from server - expected ACK\n";
+    } # while
+  } # GetServerAck
+
+  sub GetServerList {
+    my $server = shift;
+
+    my @ecrs;
+    my $srvresp;
+
+    while (defined ($srvresp = <$server>)) {
+      chomp $srvresp;
+      last if $srvresp eq "ACK";
+      if ($srvresp =~ m/ECR.*was not found/) {
+	return ();
+      } else {
+	push @ecrs, $srvresp;
+      } # if
+    } # while
+
+    return @ecrs;
+  } # GetServerList
+
+  sub GetServerResponse {
+    my $server = shift;
+
+    my %fields;
+    my $srvresp;
+
+    while (defined ($srvresp = <$server>)) {
+      chomp $srvresp;
+      last if $srvresp eq "ACK";
+      if ($srvresp =~ m/ECR.*was not found/) {
+	return ();
+      } else {
+	$srvresp =~ /(^\w+):\s+(.*)/s;
+        my $value = $2;
+	if (defined $value) {
+	  $value =~ s/\\n/\n/g;
+	} else {
+	  $value = "";
+	} # if
+	$fields {$1} = $value;
+      } # if
+    } # while
+
+    return %fields;
+  } # GetServerResponse
+
+  sub SendServerCmd {
+    my $server  = shift;
+    my $command = shift;
+
+    print $server "$command\n";
+  } # SendServerCmd
+
+1;
diff --git a/clients/Salira/CheckinPostop.pl b/clients/Salira/CheckinPostop.pl
new file mode 100644
index 0000000..61147d5
--- /dev/null
+++ b/clients/Salira/CheckinPostop.pl
@@ -0,0 +1,102 @@
+#!/usr/bin/perl -w
+################################################################################
+#
+# File:         CheckinPostop.pl
+# Description:  This script is run on check in post op. It will pick up the
+#		bug IDs from the comment and label the elements that have just
+#		been checked in.
+# Author:       Andrew@DeFaria.com
+# Created:      Fri Oct 26 15:32:12  2001
+# Language:     Perl
+# Modifications:10/22/2002: Changed to not complain about missing bug IDs if
+#		the branch was main.
+#		04/11/2003: Changed to support multiple bug IDs in the comment.
+# (c) Copyright 2003, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+use strict;
+
+BEGIN {
+  # Add the appropriate path to our modules to @INC array. We use ipconfig to
+  # get the current host's IP address then determine whether we are in the US
+  # or China. If neither then we fallback to using T:/Triggers.
+  my @ipconfig = grep (/IP Address/, `ipconfig`);
+  my ($ipaddr) = ($ipconfig[0] =~ /(\d{1,3}\.\d{1,3}.\d{1,3}\.\d{1,3})/);
+
+  # US is in the subnets of 192 and 172 while China is in the subnet of 10
+  if ($ipaddr =~ /^192|^172/) {
+    unshift (@INC, "//sons-clearcase/Views/official/Tools/lib");
+  } elsif ($ipaddr =~ /^10/) {
+    unshift (@INC, "//sons-cc/Views/official/Tools/lib");
+  } else {
+    die "Internal Error: Unable to find our modules!\n"
+  } # if
+} # BEGIN
+
+use TriggerUtils;
+
+# The following environment variables are set by Clearcase when this
+# trigger is called
+my $comment = $ENV{CLEARCASE_COMMENT};
+my $branch  = $ENV{CLEARCASE_BRTYPE};
+my $pname   = $ENV{CLEARCASE_PN};
+my $user    = $ENV{CLEARCASE_USER};
+
+sub ExtractBugIDs {
+  my $comment = shift;
+
+  my @fields  = split /\W/,$comment;
+
+  # Use associative array to insure uniqueness
+  my %bugids;
+  # Return unique array
+  my @bugids;
+
+  foreach (@fields) {
+    if (/BUGS2[0-9]{8}/) {
+      $bugids{$_} = $_;
+    } # if
+  } # foreach
+
+  foreach (keys %bugids) {
+    push @bugids, $_;
+  }
+
+  return @bugids;
+} # ExtractBugIDs
+
+sub mklabel {
+  my $label = shift;
+
+  my $result = system "cleartool lstype lbtype:$label@\\salira";
+
+  return $result if ($result eq 0);
+
+  $result = system "cleartool mklbtype -nc -shared -pbranch $label@\\salira";
+
+  if ($result eq 0) {
+    clearlog "Created label for $label";
+  } else {
+    clearlogmsg "Unable to mklbtype for $label (Error #: $result)";
+  } # if
+
+  return $result;
+} # mklabel
+
+foreach my $bugid (ExtractBugIDs ($comment)) {
+  if (mklabel ($bugid) eq 0) {
+    my $result = system "cleartool mklabel -replace $bugid \"$pname\"";
+
+    if ($result ne 0) {
+      clearlogmsg "Unable to apply label $bugid to $pname (Error #: $result)";
+      exit 1;
+    } else {
+      clearlog "Attached label $bugid to $pname";
+    } # if
+
+    clearlog "Successful postcheckin of $pname on $branch branch with bug ID $bugid";
+  } # if
+
+} # foreach
+
+exit 0;
diff --git a/clients/Salira/CheckinPreop.pl b/clients/Salira/CheckinPreop.pl
new file mode 100644
index 0000000..94e5a83
--- /dev/null
+++ b/clients/Salira/CheckinPreop.pl
@@ -0,0 +1,293 @@
+#!/usr/bin/perl
+################################################################################
+#
+# File:         CheckinPreop.pl
+# Description:  This trigger script is run when the user is attempting to
+#		checkin. Several checks are performed on the check in comment.
+#		The comment should contain the bug ID, which we will later used
+#		to label this element checkin (See CheckinPostop.pl). We will
+#		also check to insure the bug ID is valid in Clearquest and that
+#		the bug is in the proper state.
+#
+#		If the check in is on the "main" or "trial" branch then we will
+#		consult a file to insure that the bug ID is listed. This is an
+#		additional method for limiting checkins.
+# Assumptions:	Clearprompt is in the users PATH
+# Author:       Andrew@DeFaria.com
+# Created:      Fri Oct 26 15:32:12  2001
+# Language:     Perl
+# Modifications:6/25/2002: Added check to see if a bug ID label exists and it
+#		is locked. If so then that's an indication that we should not
+#		allow the checkin.
+#		6/20/2002: Added interface to cqd to verify that the bug exists
+#		in Clearquest, is of a certain state and has an owner
+#		5/15/2002: Added tests so that bug IDs must exist in
+#		mainbugs.txt or	trialbugs.txt for the main and trial branches.
+#		5/17/2002: Exempted EMS code.
+#		5/31/2002: Exempted hardware code.
+#		10/22/2002: Changed to allow checkins to main branch with no
+#		bug IDs. Removed $mainbugs.
+#		11/20/2002: It was determined to relax restrictions of checkins
+#		for non 1.0 branches such that bug ID's are not required, in fact
+#		they are not allowed.
+#		04/11/2003: Added support for multiple bug IDs in the comment
+#		05/18/2003: Changed code to only check for bug IDs in comments
+#		for check ins on certain branches.
+#
+# (c) Copyright 2003, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+use strict;
+
+my $site;
+
+BEGIN {
+  # Add the appropriate path to our modules to @INC array. We use ipconfig to
+  # get the current host's IP address then determine whether we are in the US
+  # or China.
+  my @ipconfig = grep (/IP Address/, `ipconfig`);
+  my ($ipaddr) = ($ipconfig[0] =~ /(\d{1,3}\.\d{1,3}.\d{1,3}\.\d{1,3})/);
+
+  # US is in the subnets of 192 and 172 while China is in the subnet of 10
+  if ($ipaddr =~ /^192|^172/) {
+    $site = "US";
+    unshift (@INC, "//sons-clearcase/Views/official/Tools/lib");
+  } elsif ($ipaddr =~ /^10/) {
+    $site = "CN";
+    unshift (@INC, "//sons-cc/Views/official/Tools/lib");
+  } else {
+    die "Internal Error: Unable to find our modules!\n"
+  } # if
+} # BEGIN
+
+use TriggerUtils;
+use cqc;
+
+%cqc::fields;
+
+# The following environment variables are set by Clearcase when this
+# trigger is called
+my $comment = $ENV{CLEARCASE_COMMENT};
+my $branch  = $ENV{CLEARCASE_BRTYPE};
+my $pname   = $ENV{CLEARCASE_PN};
+
+# Which vob we will look up labels in
+my $vob = "salira";
+
+my $bugid;
+
+sub ExtractBugID {
+  my $comment = shift;
+
+  my @fields  = split (/\W/,$comment);
+  my $bugid   = "unknown";
+
+  foreach (@fields) {
+    if (/BUGS2[0-9]{8}/) {
+      $bugid = $_;
+      last;
+    } # if
+  } # foreach
+
+  return $bugid;
+} # ExtractBugID
+
+sub ExtractBugIDs {
+  my $comment = shift;
+
+  my @fields  = split (/\W/,$comment);
+
+  # Use associative array to insure uniqueness
+  my %bugids;
+  # Return unique array
+  my @bugids;
+
+  foreach (@fields) {
+    if (/BUGS2[0-9]{8}/) {
+      $bugids{$_} = $_;
+    } # if
+  } # foreach
+
+  foreach (keys %bugids) {
+    push @bugids, $_;
+  }
+
+  return @bugids;
+} # ExtractBugIDs
+
+sub BugOnList {
+  my $bugid       = shift;
+  my $branch	  = shift;
+
+  my $found_bugid = 0;
+  my $bug         = "unknown";
+
+  # Excempt EMS code
+  return 1 if $pname =~ /salira\\ems/i;
+
+  # Excempt Hardware code
+  return 1 if $pname =~ /salira\\hardware/i;
+
+  # Exempt bug ID 2912
+  return 1 if $bugid eq "BUGS200002912";
+
+  # Exempt bug ID 3035
+  return 1 if $bugid eq "BUGS200003035";
+
+  my $filename;
+
+  if ($site eq "US") {
+    $filename = "//sons-clearcase/Views/official/Tools/bin/clearcase/triggers/data/$branch.lst";
+  } elsif ($site eq "CN") {
+   $filename = "//sons-cc/Views/official/Tools/bin/clearcase/triggers/data/$branch.lst";
+ } else {
+   die "Internal Error: Site not set properly! ($site)\n";
+ } # if
+
+  if (-f $filename) {
+    open (FILE, $filename) || die "Can't open $filename!\n";
+
+    while () {
+      $bug = ExtractBugID $_;
+      next if ($bug eq "unknown");
+      if ($bug eq $bugid) {
+	$found_bugid = 1;
+	last;
+      } # if
+    } # while
+
+    close (FILE);
+  } else {
+    clearlog "Skipping check because $filename does not exist!";
+    # Since there is no file list to check return that the bug id was found
+    $found_bugid = 1;
+  } # if
+
+  return $found_bugid;
+} # BugOnList
+
+sub LabelLocked {
+  # 04/28/2003: Oddity! All of a sudden this subroutine broke! I don't know
+  # why but even though we used to cd to the official view and issue our
+  # cleartool lslock command we started getting "Unable to determine VOB
+  # from pname" errors. Weird! Anyways we have changed to use the @ syntax instead. This means we must now specify the vob
+  # specifically. Fortunately we only have one vob to worry about at this
+  # time. On the plus side we no longer need to rely on the "official" view.
+  my $bugid = shift;
+
+  my $output = `cleartool lslock -short lbtype:$bugid@\\$vob 2>&1`;
+
+  if ($? == 0) {
+    return $output;
+  } else {
+    return 0;
+  } # if
+} # LabelLocked
+
+sub CheckComment {
+  my $comment = shift;
+  my $branch  = shift;
+
+  my @valid_branches = (
+    "main",
+    "rel_1.0",
+    "rel_2.0",
+    "rel_2.1",
+    "rel_2.2",
+    "rel_2.3",
+    "china_1.0",
+    "china_2.0",
+    "china_2.1",
+    "china_2.2",
+    "china_2.3",
+    "2.0_ga"
+  );
+
+  if ($comment eq "") {
+    clearlogmsg "You need to specify checkin comments";
+    return 1;
+  } # if
+
+  if (length $comment <= 4) {
+    clearlogmsg "The comment, '$comment' is too short!";
+    return 1;
+  } # if
+
+  if ($comment !~ m/.*BUGS2[0-9]{8}.*/) {
+    # Bug ID's are only required on certain branches
+    my $found = 0;
+
+    foreach (@valid_branches) {
+      if ($branch eq $_) {
+	$found = 1;
+	last;
+      } # if
+    } # foreach
+
+    if ($found == 1) {
+      clearlogmsg "Could not find bug ID in comment! This is required for the $branch branch";
+      return 1;
+    } # if
+  } # if
+
+  return 0;
+} # CheckComment
+
+sub CheckBugIDs {
+  my @bugs = @_;
+
+  my $result;
+
+  foreach my $bugid (@bugs) {
+    # Check if label is locked
+    if (LabelLocked ($bugid)) {
+      clearlog "Bug id $bugid is locked!";
+      clearmsg "Bug id $bugid is locked!\nSee your Clearcase Admin to unlock it";
+      return 1;
+    } # if
+
+    # Get Clearquest information
+    $result = cqc::GetBugRecord ($bugid, %fields);
+
+    if ($result == 0) {
+      # Make sure bug is owned
+      if ($fields {owner} eq "") {
+	clearlogmsg "No owner specified in Clearquest for bug ID $bugid.";
+	return 1;
+      } # if
+
+      # Make sure bug is in the correct state
+      if ($fields {state} ne "Assigned" and $fields {state} ne "Resolved") {
+	clearlogmsg "Bug ID $bugid is in the wrong state. It is in the " . $fields {state}. " state but should be in Assigned or Resolved state.";
+	return 1;
+      } # if
+    } elsif ($result > 0) {
+      clearlogmsg "Bug ID $bugid is not in Clearquest.";
+      return 1;
+    } else {
+      clearlogmsg "Clearquest Daemon (cqd) is not running!
+Please contact the Clearquest Administrator.";
+      return 1;
+    } # if
+
+    # Check if bug is on a branch list file
+    if (! BugOnList ($bugid, $branch)) {
+      clearlog "Bug ID $bugid is not on the list of acceptable bugs for the $branch branch!";
+      clearmsg "Bug ID $bugid is not on the list\nof acceptable bugs for the $branch branch!";
+      return 1;
+    } # if
+  } # foreach
+} # CheckBugIDs
+
+clearlog "Checkin checks started for $pname on $branch branch";
+
+if (CheckComment ($comment, $branch)) {
+  exit 1;
+} elsif (CheckBugIDs (ExtractBugIDs $comment)) {
+  exit 1;
+} # if
+
+clearlog "Successful precheckin of $pname on $branch branch with bug ID $bugid";
+
+exit 0;
diff --git a/clients/Salira/NotifyTrigger.pl b/clients/Salira/NotifyTrigger.pl
new file mode 100644
index 0000000..e2cbe69
--- /dev/null
+++ b/clients/Salira/NotifyTrigger.pl
@@ -0,0 +1,137 @@
+#!/usr/bin/perl
+################################################################################
+#
+# File:         NotifyTrigger.pl
+# Description:  This script is a generalized notify trigger. It takes one 
+#		parameter, a message file. The format of this file is similar
+#		to an email message. Environment variables will be substituted.
+# Assumptions:	Clearprompt is in the users PATH
+# Author:       Andrew@DeFaria.com
+# Created:      Tue Mar 12 15:42:55  2002
+# Language:     Perl
+# Modifications:
+#
+# (c) Copyright 2002, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+use strict;
+
+use Net::SMTP;
+
+my $mailhost;
+
+BEGIN {
+  # Add the appropriate path to our modules to @INC array. We use ipconfig to
+  # get the current host's IP address then determine whether we are in the US
+  # or China. Also set our mail server.
+  my @ipconfig = grep (/IP Address/, `ipconfig`);
+  my ($ipaddr) = ($ipconfig[0] =~ /(\d{1,3}\.\d{1,3}.\d{1,3}\.\d{1,3})/);
+
+  # US is in the subnets of 192 and 172 while China is in the subnet of 10
+  if ($ipaddr =~ /^192|^172/) {
+    $mailhost="sons-exch02.salira.com";
+    unshift (@INC, "//sons-clearcase/Views/official/Tools/lib");
+  } elsif ($ipaddr =~ /^10/) {
+    $mailhost="sons-exch03.salira.com";
+    unshift (@INC, "//sons-cc/Views/official/Tools/lib");
+  } else {
+    die "Internal Error: Unable to find our modules!\n"
+  } # if
+} # BEGIN
+
+use TriggerUtils;
+
+# This routine will replace references to environment variables. If an
+# environment variable is not defined then the string  is
+# substituted.
+sub ReplaceText {
+  my $line = shift (@_);
+
+  my ($var, $value);
+
+  while ($line =~ /\$(\w+)/) {
+    $line =~ /\$(\w+)/;
+    $var = $1;
+    if ($ENV{$var} eq "") {
+      $line =~ s/\$$var/\/;
+    } else {
+      $value = $ENV{$var};
+      $value =~ s/\\/\//g;
+      $line =~ s/\$$var/$value/;
+    } # if
+  } # while
+
+  return $line;
+} # ReplaceText
+
+sub error {
+  my $message = shift;
+
+  clearlogmsg $message;
+
+  exit 1;
+} # error 
+
+# First open the message file. If we can't then there's a problem, die!
+open (MSG, $ARGV[0]) || error "Unable to open message file:\n\n$ARGV[0]\n\n($!)";
+
+my @lines = ;
+
+# Connect to mail server
+my $smtp = Net::SMTP->new ($mailhost);
+
+error "Unable to open connection to mail host: $mailhost" if $smtp == undef;
+
+# Compose message
+my $data_sent = "F";
+my $from_seen = "F";
+my $to_seen   = "F";
+my ($line, $from, $to, @addresses);
+
+foreach $line (@lines) {
+  next if $line =~ /^\#/;
+  next if $line =~ /--/;
+
+  $line = ReplaceText $line;
+
+  if ($line =~ /^From:\s+/) {
+    $_ = $line;
+    $from = $line;
+    s/^From:\s+//;
+    $smtp->mail ($_);
+    $from_seen = "T";
+    next;
+  } # if
+
+  if ($line =~ /^To:\s+/) {
+    $_ = $line;
+    $to = $line;
+    s/^To:\s+//;
+    @addresses = split (/,|;| /);
+    $to_seen = "T";
+    foreach (@addresses) {
+      next if ($_ eq "");
+      $smtp->to ($_);
+    } # foreach
+    next;
+  } # if
+
+  if ($data_sent eq "F") {
+    $smtp->data ();
+    $smtp->datasend ($from);
+    $smtp->datasend ($to);
+    $data_sent = "T";
+  } # if
+
+  if ($from_seen eq "T" && $to_seen eq "T" && $data_sent eq "T") {
+    $smtp->datasend ($line);
+  } else {
+    clearlogmsg "Message file ($ARGV[0]) missing From and/or To!";
+    exit 1;
+  } # if
+} # foreach
+
+$smtp->dataend ();
+$smtp->quit;
+
+exit 0;
diff --git a/clients/Salira/RemoveEmptyBranch.pl b/clients/Salira/RemoveEmptyBranch.pl
new file mode 100644
index 0000000..6b1f304
--- /dev/null
+++ b/clients/Salira/RemoveEmptyBranch.pl
@@ -0,0 +1,104 @@
+#!/usr/bin/perl
+################################################################################
+#
+# File:         RemoveEmptyBranch.pl
+# Description:  This trigger script is remove empty branches. If a branch has
+#               no elements (except the 0 element of course) after an uncheckout
+#               or rmver, or the parent of a just-rmbranched branch is now empty,
+#               remove it.
+#
+#		Install like this:
+#
+#		ct mktrtype -element -global -postop uncheckout,rmver,rmbranch \
+#		-c "Remove empty branches after uncheckout, rmver, or rmbranch" \
+#		-exec T:/Triggers/RemoveEmptyBranch RM_EMPTY_BRANCH
+# Assumptions:	Clearprompt is in the users PATH
+# Author:       Andrew@DeFaria.com
+# Created:      Fri May 23 13:23:47 PDT 2003
+# Language:     Perl
+# Modifications:
+#
+# (c) Copyright 2003, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+use strict;
+
+BEGIN {
+  # Add the appropriate path to our modules to @INC array. We use ipconfig to
+  # get the current host's IP address then determine whether we are in the US
+  # or China. If neither then we fallback to using T:/Triggers.
+  my @ipconfig = grep (/IP Address/, `ipconfig`);
+  my ($ipaddr) = ($ipconfig[0] =~ /(\d{1,3}\.\d{1,3}.\d{1,3}\.\d{1,3})/);
+
+  # US is in the subnets of 192 and 172 while China is in the subnet of 10
+  if ($ipaddr =~ /^192|^172/) {
+    unshift (@INC, "//sons-clearcase/Views/official/Tools/lib");
+  } elsif ($ipaddr =~ /^10/) {
+    unshift (@INC, "//sons-cc/Views/official/Tools/lib");
+  } else {
+    die "Internal Error: Unable to find our modules!\n"
+  } # if
+} # BEGIN
+
+use TriggerUtils;
+
+# The following environment variables are set by Clearcase when this
+# trigger is called
+my $xname  = $ENV{CLEARCASE_XPN};
+my $opkind = $ENV{CLEARCASE_OP_KIND};
+my $xn_sfx = $ENV{CLEARCASE_XN_SFX};
+my $os     = $ENV{OS};
+my $brtype = $ENV{CLEARCASE_BRTYPE};
+#clearlog "Checking to see if the branch is empty and needs to be removed";
+#clearlog "xname  = $xname";
+#clearlog "opkind = $opkind";
+#clearlog "xn_sfx = $xn_sfx";
+#clearlog "os     = $os";
+
+$xname =~ s/\\/\//g if $ENV{OS} eq "Windows_NT";
+
+# For uncheckout, if the remaining version is not 0 then we are done;
+exit 0 if ($opkind eq "uncheckout" && $xname !~ m/\/0$/);
+
+#clearlog "Continuing...";
+my $branch;
+
+($branch = $xname) =~ s/\/[^\/]*$//;
+
+#clearlog "branch = $branch; xname = $xname";
+
+# Don't try to remove the /main branch
+exit 0 if $branch =~ m/\@\@\/main$/;
+
+# Check if there are other versions, branches, labels or checked out versions
+# on this branch. If so don't do anything.
+if (opendir (D, $branch)) {
+  # This opendir succeeds only in a dynamic view
+  #clearlog "In dynamic view!";
+  my @other_stuff = readdir (D);
+  closedir (D);
+
+  # In an empty branch there are four things: ".", "..", "0" an d"LATEST".
+  # If there are more then it isn't an empty branch
+  exit if (scalar (@other_stuff) != 4);
+} else {
+  # Snapshot views.
+  #clearlog "In snapshot view!";
+  my ($pname, $brpath) = split ($xn_sfx, $branch);
+  #clearlog "pname = $pname; brpath = $brpath";
+  # rmbranch will not reload the element...
+  system "cleartool update -log /dev/null \"$pname\"" if ($opkind eq "rmbranch");
+  my @vtree = `cleartool lsvtree -branch $brpath \"$pname\"`;
+  my $latest;
+  chomp ($latest = pop (@vtree));
+  $latest =~ tr/\\/\// if $os eq "Windows_NT";
+  #clearlog "latest = $latest";
+  exit 0 unless $latest =~ m/$brpath\/0$/;
+} # if
+
+# Remove the branch!
+clearlog "After $opkind branch is empty - removing empty branch $brtype";
+#clearlog "About to cleartool rmbranch -force -nc \"$branch\"";
+system "cleartool rmbranch -force -nc \"$branch\"";
+
+exit 0;
diff --git a/clients/Salira/SetOwnershipTrigger.pl b/clients/Salira/SetOwnershipTrigger.pl
new file mode 100644
index 0000000..e7cfd03
--- /dev/null
+++ b/clients/Salira/SetOwnershipTrigger.pl
@@ -0,0 +1,29 @@
+################################################################################
+#
+# File:         SetOwnershipTrigger.pl
+# Description:  This script will set the ownership of Clearcase elements to 
+#		ccadmin when a mkelem is performed. This way all Clearcase
+#		elements will be owned by ccadmin and therefore nobody but
+#		ccadmin will be able to do the destructive rmelem.
+# Author:       Andrew@DeFaria.com
+# Created:      Wed Nov 14 16:41:48  2001
+# Language:     Perl
+# Modifications:
+#
+# (c) Copyright 2001, Andrew@DeFaria.com, all rights reserved
+#
+################################################################################
+$pname = "$ENV{CLEARCASE_PN}";
+$adm   = "ccadmin";
+
+# Get current owner
+$_ = `cleartool describe $pname`;
+if (/User :\s+(\S*)\s*:/) {
+  $owner = $1;
+} else {
+  $owner = "";
+}
+
+if ($owner ne "$adm") {
+ `cleartool protect -chown $adm $ENV{CLEARCASE_PN}`;
+}
-- 
2.17.1