commit 4cc88d2f6e7d80791f0671745e26bd1c89bd58f7 Author: Fabio Scotto di Santolo Date: Tue Jul 28 19:28:25 2020 +0200 initial commit diff --git a/README.txt b/README.txt new file mode 100755 index 0000000..9f760aa --- /dev/null +++ b/README.txt @@ -0,0 +1,177 @@ +README File for Mastering UNIX Shell Scripting - Second Edition + +Author: Randal K. Michael + +======== +Publisher's note: Please note when downloading the compressed +files from our website, some versions of IE may rename the file. +If your browser renames the archive file, please make sure you +change the name back before extracting the original files +from the archive. +======== + + +The Mastering_UNIX_SS.tar.Z and Mastering_UNIX_SS.tar.gz files +are compressed tar archives that contain sample code from the +second edition of Mastering UNIX Shell Scripting. Please note +that there is an errata (a description of an error in the book's +edited code) posted in the next paragraph, followed by some notes. +Please be assured that the shell scripts and functions in this +package were never affected by the mistake in the book's edited +code pertaining to the word "Bash". + +I hope you gain some knowledge from this book, shell scripts, +and the set of functions packaged here. + +ERRATA: + +(This is the errata for the first 4,000 copies of this book.) + +During the course of editing this book, a global search-and-replace was +conducted that changed all instances of "bash" to "Bash" in the code. +Given that UNIX is case-sensitive, references in the code should be to +"bash". Note that all the original code available at the Downloads tab +of this page was never affected. + +The result was limited to the first line of Bash shell scripts, +defined as: + +#!/bin/Bash + +Instead, the correct declaration should be: + +#!/bin/bash + +In the book you may also see some Bash shell script filenames +incorrectly named with a ".Bash" filename extension instead of +the correct ".bash" filename extension. + +For example, the filename: + +my_shell_script.Bash + +should actually be: + +my_shell_script.bash + +Thanks for your understanding. + + +AUTHOR NOTES: + +I) Extracting the shell scripts and functions to your system: + +The Mastering_UNIX_SS.tar.Z and Mastering_UNIX_SS.tar.gz files +are compressed tar archives of the shell scripts and functions +in the book, and some extras in a few areas. When you extract +this archive, you will see chapter directories that contain the +code in each chapter in the book that has downloadable code. +For example, when you untar the archive, the following +subdirectories will be created (assuming you have the file +permissions, that is). + +chapter1 +chapter2 +chapter4 +chapter5 + +Notice there is not a "chapter3" subdirectory. This is because there +is no downloadable code in this chapter. + +To extract the archive, first create a directory, or just change +the directory to where you want extract the archive. + +Option 1: Extracting the compressed Mastering_UNIX_SS.tar.Z archive. + +Copy the Mastering_UNIX_SS.tar.Z file to your desired top-level +directory, and then execute the following commands. + +The following uncompress command will uncompress the archive image file: + + uncompress ./Mastering_UNIX_SS.tar.Z + +The following tar command will untar the archive image file to the +current directory: + + tar -xvpf ./Mastering_UNIX_SS.tar + +Option 2: Extracting the gzipped Mastering_UNIX_SS.tar.gz archive. + +Copy the Mastering_UNIX_SS.tar.gz file to your desired top-level +directory, and then execute the following commands. + +The following gunzip command will uncompress the archive image file: + + gunzip ./Mastering_UNIX_SS.tar.gz + +The following tar command will untar the archive image file to the +current directory: + + tar -xvpf ./Mastering_UNIX_SS.tar + +Both archives contain the same information. + +II) Using the echo command in Bash shell: + +The echo command executes differently depending on the UNIX +shell you are executing it in. A specific situation is using the +echo command's backslash operators, such as: + +echo "\n" # To produce a newline + +echo "\c" # To continue on the same line without a + # carriage return or line feed + +and many others. + +Of all the methods covered in this book to properly use the +echo command when Bash shell is involved, the most reliable +method is aliasing the echo command for use in a Bash shell +environment. Many Korn shell scripts will execute in a default +Bash shell if /bin/ksh is not installed. + +By adding the following case statement to the top of your code, it +will not be necessary to use echo -e when dealing with the echo +command's backslash operators if your ksh script should happen +to execute in a Bash shell. This is common on many Linux systems. +You can also use this in your Bash scripts if you want to omit +adding the -e to your echo statement when writing your shell scripts. + +case $(basename $SHELL) in +bash) alias echo="echo -e" + ;; +esac + +Now anytime the echo command is used in a script that is +executing in Bash shell, echo -e will be substituted, +enabling the backslash operators \n, \c, \t, and so on. + +III) How individual functions are separated into individual files: + +When this book's script/function archive is installed to your system, +each function that is its own file will have the following filename +format: + +"function_" followed by the name of the actual function in the script. + +For example: + +If the real function name in the script or book is: + +check_file_system + +Then the filename for this individual function would be: + +function_check_file_system + +---- + +Thanks for buying my book. I hope the answer to every scripting problem +you encounter will be intuitively obvious when you finish +reading this second edition! + + +Cheers, + +Randy + diff --git a/chapter1/function_elapsed_time b/chapter1/function_elapsed_time new file mode 100755 index 0000000..7703e17 --- /dev/null +++ b/chapter1/function_elapsed_time @@ -0,0 +1,11 @@ +elapsed_time () +{ +SEC=$1 +(( SEC < 60 )) && echo "[Elapsed time: $SEC seconds]\c" + +(( SEC >= 60 && SEC < 3600 )) && echo "[Elapsed time: $(( SEC / 60 )) \ +min $(( SEC % 60 )) sec]\c" + +(( SEC > 3600 )) && echo "[Elapsed time: $(( SEC / 3600 )) hr \ +$(( (SEC % 3600) / 60 )) min $(( (SEC % 3600) % 60 )) sec]\c" +} diff --git a/chapter1/function_ping_host b/chapter1/function_ping_host new file mode 100755 index 0000000..1258225 --- /dev/null +++ b/chapter1/function_ping_host @@ -0,0 +1,26 @@ +function ping_host +{ +HOST=$1 # Grab the host to ping from ARG1. +PING_COUNT=3 +PACKET_SIZE=54 + +# This next case statement executes the correct ping +# command based on the Unix flavor + +case $(uname) in + +AIX|OpenBSD|Linux) + ping -c${PING_COUNT} $HOST 2>/dev/null + ;; +HP-UX) + ping $HOST $PACKET_SIZE $PING_COUNT 2>/dev/null + ;; +SunOS) + ping -s $HOST $PACKET_SIZE $PING_COUNT 2>/dev/null + ;; +*) + echo "\nERROR: Unsupported Operating System - $(uname)" + echo "\n\t. . .EXITING. . .\n" + exit 1 +esac +} diff --git a/chapter1/function_rotate_line b/chapter1/function_rotate_line new file mode 100755 index 0000000..65bb2dc --- /dev/null +++ b/chapter1/function_rotate_line @@ -0,0 +1,26 @@ +function rotate_line +{ +INTERVAL=1 # Sleep time between "twirls" +TCOUNT="0" # For each TCOUNT the line twirls one increment + +while : # Loop forever. . .until this function is killed +do + TCOUNT=`expr $TCOUNT + 1` # Increment the TCOUNT + + case $TCOUNT in + "1") echo .-."\b\c" + sleep $INTERVAL + ;; + "2") echo .\\."\b\c" + sleep $INTERVAL + ;; + "3") echo "|\b\c" + sleep $INTERVAL + ;; + "4") echo "/\b\c" + sleep $INTERVAL + ;; + *) TCOUNT="0" ;; # Reset the TCOUNT to "0", zero. + esac +done +} diff --git a/chapter1/generic_rsync.bash b/chapter1/generic_rsync.bash new file mode 100755 index 0000000..9dbe37b --- /dev/null +++ b/chapter1/generic_rsync.bash @@ -0,0 +1,33 @@ +#!/bin/bash +# +# SCRIPT: generic_rsync.bash +# AUTHOR: Randy Michael +# DATE: 11/18/2007 +# REV: 1.0 +# PURPOSE: This is a generic shell script to copy files +# using rsync. +# +# set -n # Uncomment to check script syntax without execution +# set -x # Uncomment to debug this script +# +# REV LIST: +# +# +############################################## +# DEFINE FILES AND VARIABLES HERE +############################################## + +# Define the source and destination files/directories + +SOURCE_FL="/scripts/" +DESTIN_FL="booboo:/scripts" + +############################################## +# BEGINNING OF MAIN +############################################## + +# Start the rsync copy + +rsync -avz "$SOURCE_FL" "$DESTIN_FL" + +# End of generic_rsync.bash diff --git a/chapter1/keyit.dsa b/chapter1/keyit.dsa new file mode 100755 index 0000000..5e487bc --- /dev/null +++ b/chapter1/keyit.dsa @@ -0,0 +1,11 @@ +#!/bin/bash +# +# SCRIPT: keyit.dsa +# +# PURPOSE: This script is used to set up DSA SSH keys. This script must +# be executed by the user who needs the keys setup. + +REM_HOST=$1 + +cat $HOME/.ssh/id_dsa.pub | ssh $REM_HOST "cat >> ~/.ssh/authorized_keys" + diff --git a/chapter1/keyit.rsa b/chapter1/keyit.rsa new file mode 100755 index 0000000..426e438 --- /dev/null +++ b/chapter1/keyit.rsa @@ -0,0 +1,11 @@ +#!/bin/bash +# +# SCRIPT: keyit.rsa +# +# PURPOSE: This script is used to set up RSA SSH keys. +# This script must be executed by the user who needs the keys setup. + +REM_HOST=$1 + +cat $HOME/.ssh/id_rsa.pub | ssh $REM_HOST "cat >> ~/.ssh/authorized_keys" + diff --git a/chapter1/script.stub b/chapter1/script.stub new file mode 100755 index 0000000..8323bd0 --- /dev/null +++ b/chapter1/script.stub @@ -0,0 +1,50 @@ +#!/bin/bash +# +# SCRIPT: NAME_of_SCRIPT +# AUTHOR: AUTHORS_NAME +# DATE: DATE_of_CREATION +# REV: 1.1.A (Valid are A, B, D, T, Q, and P) +# (For Alpha, Beta, Dev, Test, QA, and Production) +# +# PLATFORM: (SPECIFY: AIX, HP-UX, Linux, OpenBSD, Solaris, other flavor, +# or Not platform dependent) +# +# REQUIREMENTS: If this script has requirements that need to be noted, this +# is the place to spell those requirements in detail. +# +# EXAMPLE: OpenSSH is required for this shell script to work. +# +# PURPOSE: Give a clear, and if necessary, long, description of the +# purpose of the shell script. This will also help you stay +# focused on the task at hand. +# +# REV LIST: +# DATE: DATE_of_REVISION +# BY: AUTHOR_of_MODIFICATION +# MODIFICATION: Describe what was modified, new features, etc-- +# +# +# set -n # Uncomment to check script syntax, without execution. +# # NOTE: Do not forget to put the # comment back in or +# # the shell script will never execute! +# set -x # Uncomment to debug this shell script +# +########################################################## +# DEFINE FILES AND VARIABLES HERE +########################################################## + +THIS_SCRIPT=$(basename $0) + +########################################################## +# DEFINE FUNCTIONS HERE +########################################################## + + +########################################################## +# BEGINNING OF MAIN +########################################################## + + + +# End of script + diff --git a/chapter1/select_system_info_menu.bash b/chapter1/select_system_info_menu.bash new file mode 100755 index 0000000..1c7bc05 --- /dev/null +++ b/chapter1/select_system_info_menu.bash @@ -0,0 +1,72 @@ +#!/bin/bash +# +# SCRIPT: select_system_info_menu.bash +# AUTHOR: Randy Michael +# DATE: 1/17/2008 +# REV: 1.0 +# +# PURPOSE: This shell script uses the shell's select +# command to create a menu to show system information + +# Clear the screen +clear + +# Display the menu title header +echo -e "\n\tSYSTEM INFORMATION MENU\n" + +# Define the menu prompt + +PS3="Select an option and press Enter: " + +# The select command defines what the menu +# will look like + +select i in OS Host Filesystems Date Users Quit +do + case $i in + OS) echo + uname + ;; + Host) echo + hostname + ;; + Filesystems) + echo + df -k | more + ;; + Date) echo + date + ;; + Users) echo + who + ;; + Quit) break + ;; + esac + + # Setting the select command's REPLY variable + # to NULL causes the menu to be redisplayed + + REPLY= + + # Pause before redisplaying the menu + + echo -e "\nPress Enter to Continue...\c" + read + + # Ready to redisplay the menu again + + # clear the screen + + clear + + # Display the menu title header + + echo -e "\n\tSYSTEM INFORMATION MENU\n" + +done + +# Clear the screen before exiting + +clear + diff --git a/chapter1/test_string.ksh b/chapter1/test_string.ksh new file mode 100755 index 0000000..d466e74 --- /dev/null +++ b/chapter1/test_string.ksh @@ -0,0 +1,180 @@ +#!/bin/ksh +# +# SCRIPT: test_string.ksh +# AUTHOR: Randy Michael +# REV: 1.0.D - Used for developement +# DATE: 10/15/2007 +# PLATFORM: Not Platform Dependent +# +# PURPOSE: This script is used to test a character +# string, or variable, for its composition. +# Examples include numeric, lowercase or uppercase +# characters, alpha-numeric characters, and IP address. +# +# REV LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to verify syntax without any execution. +# # REMEMBER: Put the comment back or the script will +# # NOT EXECUTE! +# +#################################################### +############## DEFINE FUNCTIONS HERE ############### +#################################################### + +test_string () +{ +# This function tests a character string + +# Must have one argument ($1) + +if (( $# != 1 )) +then + # This error would be a programming error + + print "ERROR: $(basename $0) requires one argument" + return 1 +fi +# Assign arg1 to the variable --> STRING + +STRING=$1 + +# This is where the string test begins + + + +case $STRING in + ++([0-9]).+([0-9]).+([0-9]).+([0-9])) + # Testing for an IP address - valid and invalid + INVALID=FALSE + + # Separate the integer portions of the "IP" address + # and test to ensure that nothing is greater than 255 + # or it is an invalid IP address. + + for i in $(echo $STRING | awk -F . '{print $1, $2, $3, $4}') + do + if (( i > 255 )) + then + INVALID=TRUE + fi + done + + case $INVALID in + TRUE) print 'INVALID_IP_ADDRESS' + ;; + FALSE) print 'VALID_IP_ADDRESS' + ;; + esac + ;; ++([0-1])) # Testing for 0-1 only + print 'BINARY_OR_POSITIVE_INTEGER' + ;; ++([0-7])) # Testing for 0-7 only + print 'OCTAL_OR_POSITIVE_INTEGER' + ;; ++([0-9])) # Check for an integer + print 'INTEGER' + ;; ++([-0-9])) # Check for a negative whole number + print 'NEGATIVE_WHOLE_NUMBER' + ;; ++([0-9]|[.][0-9])) + # Check for a positive floating point number + print 'POSITIVE_FLOATING_POINT' + ;; ++(+[0-9][.][0-9])) + # Check for a positive floating point number + # with a + prefix + print 'POSITIVE_FLOATING_POINT' + ;; ++(-[0-9][.][0-9])) + # Check for a negative floating point number + print 'NEGATIVE_FLOATING_POINT' + ;; ++([-.0-9])) + # Check for a negative floating point number + print 'NEGATIVE_FLOATING_POINT' + ;; ++([+.0-9])) + # Check for a positive floating point number + print 'POSITIVE_FLOATING_POINT' + ;; ++([a-f])) # Test for hexidecimal or all lowercase characters + print 'HEXIDECIMAL_OR_ALL_LOWERCASE' + ;; ++([a-f]|[0-9])) # Test for hexidecimal or all lowercase characters + print 'HEXIDECIMAL_OR_ALL_LOWERCASE_ALPHANUMERIC' + ;; ++([A-F])) # Test for hexidecimal or all uppercase characters + print 'HEXIDECIMAL_OR_ALL_UPPERCASE' + ;; ++([A-F]|[0-9])) # Test for hexidecimal or all uppercase characters + print 'HEXIDECIMAL_OR_ALL_UPPERCASE_ALPHANUMERIC' + ;; ++([a-f]|[A-F])) + # Testing for hexidecimal or mixed-case characters + print 'HEXIDECIMAL_OR_MIXED_CASE' + ;; ++([a-f]|[A-F]|[0-9])) + # Testing for hexidecimal/alpha-numeric strings only + print 'HEXIDECIMAL_OR_MIXED_CASE_ALPHANUMERIC' + ;; ++([a-z])) # Testing for all lowercase characters only + print 'ALL_LOWERCASE' + ;; ++([A-Z])) # Testing for all uppercase numbers only + print 'ALL_UPPERCASE' + ;; ++([a-z]|[A-Z])) + # Testing for mixed case alpha strings only + print 'MIXED_CASE' + ;; ++([a-z]|[A-Z]|[0-9])) + # Testing for any alpha-numeric string only + print 'ALPHA-NUMERIC' + ;; + *) # None of the tests matched the string coposition + print 'INVALID_STRING_COMPOSITION' + ;; +esac +} + +#################################################### + +usage () +{ +echo "\nERROR: Please supply one character string or variable\n" +echo "USAGE: $THIS_SCRIPT {character string or variable}\n" +} + +#################################################### +############# BEGINNING OF MAIN #################### +#################################################### + +# Query the system for the name of this shell script. +# This is used for the "usage" function. + +THIS_SCRIPT=$(basename $0) + +# Check for exactly one command-line argument + +if (( $# != 1 )) +then + usage + exit 1 +fi + +# Everything looks okay if we got here. Assign the +# single command-line argument to the variable "STRING" + +STRING=$1 + +# Call the "test_string" function to test the composition +# of the character string stored in the $STRING variable. + +test_string $STRING + +# End of script diff --git a/chapter10/function_mon_proc_end b/chapter10/function_mon_proc_end new file mode 100755 index 0000000..5a8ceff --- /dev/null +++ b/chapter10/function_mon_proc_end @@ -0,0 +1,21 @@ +mon_proc_end () +{ + END_RC="0" + until (( END_RC != 0 )) + do + ps aux | grep -v "grep $PROCESS" | grep -v $SCRIPT_NAME \ + | grep $PROCESS >/dev/null 2>&1 + + END_RC=$? # Check the Return Code!! + sleep 1 # Needed to reduce CPU load! + done + + echo 'N' # Turn the RUN flag off + + # Grab a Timestamp + TIMESTAMP=$(date +%D@%T) + + echo "END PROCESS: $PROCESS ended ==> $TIMESTAMP" >> $LOGFILE & + echo "END PROCESS: $PROCESS ended ==> $TIMESTAMP" > $TTY +} + diff --git a/chapter10/function_mon_proc_start b/chapter10/function_mon_proc_start new file mode 100755 index 0000000..7a89404 --- /dev/null +++ b/chapter10/function_mon_proc_start @@ -0,0 +1,21 @@ +mon_proc_start () +{ + START_RC="-1" # Initialize to -1 + until (( START_RC == 0 )) + do + ps aux | grep -v "grep $PROCESS" | grep -v $SCRIPT_NAME \ + | grep $PROCESS >/dev/null 2>&1 + + START_RC=$? # Check the Return Code!!! + sleep 1 # Needed to reduce CPU load! + done + + echo 'Y' # Turn the RUN flag on + + # Grab the timestamp + TIMESTAMP=$(date +%D@%T) + + echo "START PROCESS: $PROCESS began ==> $TIMESTAMP" >> $LOGFILE & + echo "START PROCESS: $PROCESS began ==> $TIMESTAMP" > $TTY +} + diff --git a/chapter10/function_post_event_script b/chapter10/function_post_event_script new file mode 100755 index 0000000..60f79e7 --- /dev/null +++ b/chapter10/function_post_event_script @@ -0,0 +1,12 @@ +post_event_script () +{ +# Put anything that you want to execute AFTER the +# monitored process ENDS in this function + +: # No-OP - Need as a placeholder for an empty function + # Comment Out the Above colon, ':' + +POST_RC=$? +return $POST_RC +} + diff --git a/chapter10/function_pre_event_script b/chapter10/function_pre_event_script new file mode 100755 index 0000000..0c16acc --- /dev/null +++ b/chapter10/function_pre_event_script @@ -0,0 +1,12 @@ +pre_event_script () +{ +# Put anything that you want to execute BEFORE the +# monitored process STARTS in this function + +: # No-OP - Needed as a placeholder for an empty function + # Comment Out the Above colon, ':' + +PRE_RC=$? +return $PRE_RC +} + diff --git a/chapter10/function_proc_watch b/chapter10/function_proc_watch new file mode 100755 index 0000000..e849262 --- /dev/null +++ b/chapter10/function_proc_watch @@ -0,0 +1,186 @@ +proc_watch () +{ +# set -x # Uncomment to debug this function +# This function does all of the process monitoring! + +while : # Loop Forever!! +do + case $RUN in + 'Y') + # This will run the startup_event_script, which is a function + if [[ $RUN_STARTUP_EVENT = 'Y' ]] + then + echo "STARTUP EVENT: Executing Startup Event Script..."\ + > $TTY + echo "STARTUP EVENT: Executing Startup Event Script..."\ + >> $LOGFILE + + startup_event_script # USER DEFINED FUNCTION!!! + RC=$? # Check the Return Code!! + if (( "RC" == 0 )) + then + echo "SUCCESS: Startup Event Script Completed RC -\ +${RC}" > $TTY + echo "SUCCESS: Startup Event Script Completed RC -\ +${RC}" >> $LOGFILE + + else + echo "FAILURE: Startup Event Script FAILED RC -\ +${RC}" > $TTY + echo "FAILURE: Startup Event Script FAILED RC -\ +${RC}" >> $LOGFILE + fi + fi + integer PROC_COUNT='-1' # Reset the Counters + integer LAST_COUNT='-1' + # Loop until the process(es) end(s) + + until (( "PROC_COUNT" == 0 )) + do + # This function is a Co-Process. $BREAK checks to see if + # "Program Interrupt" has taken place. If so BREAK will + # be 'Y' and we exit both the loop and function. + + read BREAK + if [[ $BREAK = 'Y' ]] + then + return 3 + fi + + PROC_COUNT=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME \ + | grep $PROCESS | wc -l) >/dev/null 2>&1 + + if (( "LAST_COUNT" > 0 && "LAST_COUNT" != "PROC_COUNT" )) + then + # The Process Count has Changed... + TIMESTAMP=$(date +%D@%T) + # Get a list of the PID of all of the processes + PID_LIST=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME \ + | grep $PROCESS | awk '{print $2}') + + echo "PROCESS COUNT: $PROC_COUNT $PROCESS\ +Processes Running ==> $TIMESTAMP" >> $LOGFILE & + echo "PROCESS COUNT: $PROC_COUNT $PROCESS\ +Processes Running ==> $TIMESTAMP" > $TTY + echo ACTIVE PIDS: $PID_LIST >> $LOGFILE & +echo ACTIVE PIDS: $PID_LIST > $TTY + fi + LAST_COUNT=$PROC_COUNT + sleep $INTERVAL # Needed to reduce CPU load! + done + + RUN='N' # Turn the RUN Flag Off + TIMESTAMP=$(date +%D@%T) + echo "ENDING PROCESS: $PROCESS END time ==>\ +$TIMESTAMP" >> $LOGFILE & + echo "ENDING PROCESS: $PROCESS END time ==>\ +$TIMESTAMP" > $TTY + + # This will run the post_event_script, which is a function + + if [[ $RUN_POST_EVENT = 'Y' ]] + then + echo "POST EVENT: Executing Post Event Script..."\ + > $TTY + echo "POST EVENT: Executing Post Event Script..."\ + >> $LOGFILE & + + post_event_script # USER DEFINED FUNCTION!!! + integer RC=$? + if (( "RC" == 0 )) + then + echo "SUCCESS: Post Event Script Completed RC -\ +${RC}" > $TTY + echo "SUCCESS: Post Event Script Completed RC -\ +${RC}" >> $LOGFILE + else + echo "FAILURE: Post Event Script FAILED RC - ${RC}"\ + > $TTY + echo "FAILURE: Post Event Script FAILED RC - ${RC}"\ + >> $LOGFILE + fi + fi + ;; + + 'N') + # This will run the pre_event_script, which is a function + + if [[ $RUN_PRE_EVENT = 'Y' ]] + then + echo "PRE EVENT: Executing Pre Event Script..." > $TTY + echo "PRE EVENT: Executing Pre Event Script..." >> $LOGFILE + + pre_event_script # USER DEFINED FUNCTION!!! + RC=$? # Check the Return Code!!! + if (( "RC" == 0 )) + then + echo "SUCCESS: Pre Event Script Completed RC - ${RC}"\ + > $TTY + echo "SUCCESS: Pre Event Script Completed RC - ${RC}"\ + >> $LOGFILE + else + echo "FAILURE: Pre Event Script FAILED RC - ${RC}"\ + > $TTY + echo "FAILURE: Pre Event Script FAILED RC - ${RC}"\ + >> $LOGFILE + fi + fi + + echo "WAITING: Waiting for $PROCESS to \ +startup...Monitoring..." + + integer PROC_COUNT='-1' # Initialize to a fake value + + # Loop until at least one process starts + + until (( "PROC_COUNT" > 0 )) + do + # This is a Co-Process. This checks to see if a "Program + # Interrupt" has taken place. If so BREAK will be 'Y' and + # we exit both the loop and function + + read BREAK + if [[ $BREAK = 'Y' ]] + then + return 3 + fi + + PROC_COUNT=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME | grep $PROCESS | wc -l) \ + >/dev/null 2>&1 + + sleep $INTERVAL # Needed to reduce CPU load! + done + + RUN='Y' # Turn the RUN Flag On + + TIMESTAMP=$(date +%D@%T) + + PID_LIST=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME \ + | grep $PROCESS | awk '{print $2}') + + if (( "PROC_COUNT" == 1 )) + then + echo "START PROCESS: $PROCESS START time ==>\ +$TIMESTAMP" >> $LOGFILE & + echo ACTIVE PIDS: $PID_LIST >> $LOGFILE & + echo "START PROCESS: $PROCESS START time ==>\ +$TIMESTAMP" > $TTY + echo ACTIVE PIDS: $PID_LIST > $TTY + elif (( "PROC_COUNT" > 1 )) + then + echo "START PROCESS: $PROC_COUNT $PROCESS\ +Processes Started: START time ==> $TIMESTAMP" >> $LOGFILE & + echo ACTIVE PIDS: $PID_LIST >> $LOGFILE & + echo "START PROCESS: $PROC_COUNT $PROCESS\ +Processes Started: START time ==> $TIMESTAMP" > $TTY + echo ACTIVE PIDS: $PID_LIST > $TTY + fi + ;; + esac +done +} + diff --git a/chapter10/function_startup_event_script b/chapter10/function_startup_event_script new file mode 100755 index 0000000..e87668c --- /dev/null +++ b/chapter10/function_startup_event_script @@ -0,0 +1,12 @@ +startup_event_script () +{ +# Put anything that you want to execute WHEN, or AS, the +# monitored process STARTS in this function + +: # No-OP - Needed as a placeholder for an empty function + # Comment Out the Above colon, ':' + +STARTUP_RC=$? +return $STARTUP_RC +} + diff --git a/chapter10/function_test_string b/chapter10/function_test_string new file mode 100755 index 0000000..aeec7b6 --- /dev/null +++ b/chapter10/function_test_string @@ -0,0 +1,29 @@ +test_string () +{ +if (( $# != 1 )) +then + echo 'ERROR' + return +fi + +C_STRING=$1 + +# Test the character string for its composition + +case $C_STRING in + + +([0-9])) echo 'POS_INT' # Integer >= 0 + ;; + +([-0-9])) echo 'NEG_INT' # Integer < 0 + ;; + +([a-z])) echo 'LOW_CASE' # lower case text + ;; + +([A-Z])) echo 'UP_CASE' # UPPER case text + ;; + +([a-z]|[A-Z])) echo 'MIX_CASE' # MIxed CAse text + ;; + *) echo 'UNKNOWN' # Anything else + ;; +esac +} + diff --git a/chapter10/proc_mon.ksh b/chapter10/proc_mon.ksh new file mode 100755 index 0000000..28365ba --- /dev/null +++ b/chapter10/proc_mon.ksh @@ -0,0 +1,152 @@ +#!/usr/bin/ksh +# +# SCRIPT: proc_mon.ksh +# AUTHOR: Randy Michael +# DATE: 02/14/2007 +# REV: 1.1.P +# PLATFORM: Not Platform Dependent +# +# PURPOSE: This script is used to monitor a process to end +# specified by ARG1 if a single command-line argument is +# used. There is also a "verbose" mode where the monitored +# process is displayed and ARG2 is monitored. +# +# USAGE: proc_mon.ksh [-v] process-to-monitor +# +# EXIT STATUS: +# 0 ==> Monitored process has terminated +# 1 ==> Script usage error +# 2 ==> Target process to monitor is not active +# 3 ==> This script exits on a trapped signal +# +# REV. LIST: +# +# 02/22/2007 - Added code for a "verbose" mode to output the +# results of the .ps aux. command. The verbose +# mode is set using a "-v" switch. +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to debug without any command execution + +SCRIPT_NAME=`basename $0` + +######################################################## +############ DEFINE FUNCTIONS HERE ##################### +######################################################## + +function usage +{ + echo "\n\n" + echo "USAGE: $SCRIPT_NAME [-v] {Process_to_monitor}" + echo "\nEXAMPLE: $SCRIPT_NAME my_backup\n" + echo "OR" + echo "\nEXAMPLE: $SCRIPT_NAME -v my_backup\n" + echo "Try again...EXITING...\n" +} +######################################################## + +function exit_trap +{ + echo "\n...EXITING on trapped signal...\n" +} +######################################################## +################ START OF MAIN########################## +######################################################## + +################ +# Set a trap...# +################ + +trap 'exit_trap; exit 3' 1 2 3 15 + +# First Check for the Correct Number of Arguments +# One or Two is acceptable + +if (( $# != 1 && $# != 2 )) +then + usage + exit 1 +fi + +# Parse through the command-line arguments and see if verbose +# mode has been specified. NOTICE that we assign the target +# process to the PROCESS variable!!! +# Embedded case statement... + +case $# in + 1) case $1 in + '-v') usage + exit 1 + ;; + *) PROCESS=$1 + ;; + esac + ;; + 2) case $1 in + '-v') continue + ;; + esac + + case $2 in + '-v') usage + exit 1 + ;; + *) PROCESS=$2 + ;; + esac + ;; + *) usage + exit 1 + ;; +esac + +# Check if the process is running or exit! + +ps aux | grep "$PROCESS" | grep -v "grep $PROCESS" \ +| grep -v $SCRIPT_NAME >/dev/null + +if (( $? != 0 )) +then + echo "\n\n$PROCESS is NOT an active process...EXITING...\n" + exit 2 +fi + +# Show verbose mode if specified... + +if (( $# == 2 )) && [[ $1 = "-v" ]] +then + # Verbose mode has been specified! + echo "\n" + + # Extract the columns heading from the ps aux output + ps aux | head -n 1 + + ps aux | grep "$PROCESS" | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME +fi + +##### O.K. The process is running, start monitoring... + +SLEEP_TIME="1" # Seconds between monitoring + +RC="0" # RC is the Return Code +echo "\n\n" # Give a couple of blank lines + +echo "$PROCESS is currently RUNNING...`date`\n" + +#################################### +# Loop UNTIL the $PROCESS stops... + +while (( RC == 0 )) # Loop until the return code is not zero +do + ps aux | grep $PROCESS | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME >/dev/null 2>&1 + if (( $? != 0 )) # Check the Return Code!!!!! + then + echo "\n...$PROCESS has COMPLETED...`date`\n" + exit 0 + fi + sleep $SLEEP_TIME # Needed to reduce CPU Load!!! +done + +# End of Script diff --git a/chapter10/proc_watch.ksh b/chapter10/proc_watch.ksh new file mode 100755 index 0000000..71b648f --- /dev/null +++ b/chapter10/proc_watch.ksh @@ -0,0 +1,151 @@ +#!/bin/ksh +# +# SCRIPT: proc_watch.ksh +# AUTHOR: Randy Michael +# DATE: 09-12-2007 +# REV: 1.0.P +# PLATFORM: Not Platform Dependent +# +# PURPOSE: This script is used to monitor and log +# the status of a process as it starts and stops. +# +# REV LIST: +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check syntax without ANY execution +# +#################################################### +########## DEFINE FILES AND VARIABLES HERE ######### +#################################################### + +LOGFILE="/tmp/proc_status.log" +[[ ! -s $LOGFILE ]] && touch $LOGFILE + +PROCESS="$1" # Process to Monitor +SCRIPT_NAME=$(basename $0) # Script Name w/o the PATH +TTY=$(tty) # Current tty or pty + +#################################################### +############# DEFINE FUNCTIONS HERE ################ +#################################################### + +usage () +{ + echo "\nUSAGE: $SCRIPT_NAME process_to_monitor\n" +} + +#################################################### + +trap_exit () +{ + # Log an ending time for process monitoring + TIMESTAMP=$(date +%D@%T) # Get a new timestamp... + echo "MON_STOP: Monitoring for $PROCESS ended ==> $TIMESTAMP" \ + | tee -a $LOGFILE + + # Kill all functions + kill -9 $(jobs -p) 2>/dev/null +} + +#################################################### + +mon_proc_end () +{ + END_RC="0" + until (( END_RC != 0 )) + do + ps aux | grep -v "grep $PROCESS" | grep -v $SCRIPT_NAME \ + | grep $PROCESS >/dev/null 2>&1 + + END_RC=$? # Check the Return Code!! + sleep 1 # Needed to reduce CPU load! + done + + echo 'N' # Turn the RUN flag off + + # Grab a Timestamp + TIMESTAMP=$(date +%D@%T) + + echo "END PROCESS: $PROCESS ended ==> $TIMESTAMP" >> $LOGFILE & + echo "END PROCESS: $PROCESS ended ==> $TIMESTAMP" > $TTY +} +#################################################### + +mon_proc_start () +{ + START_RC="-1" # Initialize to -1 + until (( START_RC == 0 )) + do + ps aux | grep -v "grep $PROCESS" | grep -v $SCRIPT_NAME \ + | grep $PROCESS >/dev/null 2>&1 + + START_RC=$? # Check the Return Code!!! + sleep 1 # Needed to reduce CPU load! + done + + echo 'Y' # Turn the RUN flag on + + # Grab the timestamp + TIMESTAMP=$(date +%D@%T) + + echo "START PROCESS: $PROCESS began ==> $TIMESTAMP" >> $LOGFILE & + echo "START PROCESS: $PROCESS began ==> $TIMESTAMP" > $TTY +} + +#################################################### +############## START OF MAIN ####################### +#################################################### + +### SET A TRAP #### + +trap 'trap_exit; exit 0' 1 2 3 15 + +# Check for the Correct Command Line Argument - Only 1 + +if (( $# != 1 )) +then + usage + exit 1 +fi + +# Get an Initial Process State and Set the RUN Flag + +ps aux | grep -v "grep $PROCESS" | grep -v $SCRIPT_NAME \ + | grep $PROCESS >/dev/null + +PROC_RC=$? # Check the Return Code!! + +# Give some initial feedback before starting the loop + +if (( PROC_RC == 0 )) +then + echo "The $PROCESS process is currently running...Monitoring..." + RUN="Y" # Set the RUN Flag to YES +else + echo "The $PROCESS process is not currently running...Monitoring..." + RUN="N" # Set the RUN Flag to NO +fi + +TIMESTAMP=$(date +%D@%T) # Grab a timestamp for the log + +# Use a "tee -a $#LOGFILE" to send output to both standard output +# and to the file referenced by $LOGFILE + +echo "MON_START: Monitoring for $PROCESS began ==> $TIMESTAMP" \ + | tee -a $LOGFILE + +# Loop Forever!! + +while : +do + case $RUN in + 'Y') # Loop Until the Process Ends + RUN=$(mon_proc_end) + ;; + 'N') # Loop Until the Process Starts + RUN=$(mon_proc_start) + ;; + esac +done + +# End of Script diff --git a/chapter10/proc_watch_timed.ksh b/chapter10/proc_watch_timed.ksh new file mode 100755 index 0000000..c82f938 --- /dev/null +++ b/chapter10/proc_watch_timed.ksh @@ -0,0 +1,570 @@ +#!/bin/ksh +# +# SCRIPT: proc_watch_timed.ksh +# AUTHOR: Randy Michael +# DATE: 09-14-2007 +# REV: 1.0.P +# PLATFORM: Not Platform Dependent +# +# PURPOSE: This script is used to monitor and log +# the status of a process as it starts and stops. +# Command line options are used to identify the target +# process to monitor and the length of time to monitor. +# Each event is logged to the file defined by the +# $LOGFILE variable. This script also has the ability +# to execute pre, startup, and post events. These are +# controlled by the $RUN_PRE_EVENT, $RUN_STARTUP_EVENT, +# and $RUN_POST_EVENT variables. These variables control +# execution individually. Whatever is to be executed is to +# be placed in either the "pre_event_script", +# startup_event_script, or the +# "post_event_script" functions, or in any combination. +# Timing is controlled on the command line. +# +# USAGE: $SCRIPT_NAME total_seconds target_process +# +# Will monitor the specified process for the +# specified number of seconds. +# +# USAGE: $SCRIPT_NAME [-s|-S seconds] [-m|-M minutes] +# [-h|-H hours] [-d|-D days] +# [-p|-P process] +# +# Will monitor the specified process for number of +# seconds specified within -s seconds, -m minutes, +# -h hours, and -d days. Any combination of command +# switches can be used. +# +# REV LIST: +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check syntax without ANY execution +# +#################################################### +########## DEFINE FILES AND VARIABLES HERE ######### +#################################################### + +typeset -u RUN_PRE_EVENT # Force to UPPERCASE +typeset -u RUN_STARTUP_EVENT # Force to UPPERCASE +typeset -u RUN_POST_EVENT # force to UPPERCASE + +RUN_PRE_EVENT='N' # A 'Y' will execute, anything else will not +RUN_STARTUP_EVENT='Y' # A 'Y' will execute, anything else will not +RUN_POST_EVENT='Y' # A 'Y' will execute, anything else will not + +LOGFILE="/tmp/proc_status.log" +[[ ! -s $LOGFILE ]] && touch $LOGFILE + +SCRIPT_NAME=$(basename $0) +TTY=$(tty) +INTERVAL="1" # Seconds between sampling +JOBS= + +#################################################### +############# DEFINE FUNCTIONS HERE ################ +#################################################### +usage () +{ +echo "\n\n\t*****USAGE ERROR*****" +echo "\n\nUSAGE: $SCRIPT_NAME seconds process" +echo "\nWill monitor the specified process for the" +echo "specified number of seconds." +echo "\nUSAGE: $SCRIPT_NAME [-s|-S seconds] [-m|-M minutes]" +echo " [-h|-H hours] [-d|-D days] [-p|-P process]\n" +echo "\nWill monitor the specified process for number of" +echo "seconds specified within -s seconds, -m minutes," +echo "-h hours and -d days. Any combination of command" +echo "switches can be used.\n" +echo "\nEXAMPLE: $SCRIPT_NAME 300 dtcalc" +echo "\n\nEXAMPLE: $SCRIPT_NAME -m 5 -p dtcalc" +echo "\nBoth examples will monitor the dtcalc process" +echo "for 5 minutes. Can specify days, hours, minutes" +echo "and seconds, using -d, -h, -m and -s\n\n" +} +#################################################### +trap_exit () +{ +# set -x # Uncommant to debug this function +# Log an ending time for process monitoring +echo "INTERRUPT: Program Received an Interrupt...EXITING..." > $TTY +echo "INTERRUPT: Program Received an Interrupt...EXITING..." >> $LOGFILE +TIMESTAMP=$(date +%D@%T) # Get a new timestamp... +echo "MON_STOPPED: Monitoring for $PROCESS ended ==> $TIMESTAMP\n" \ + >> $TTY +echo "MON_STOPPED: Monitoring for $PROCESS ended ==> $TIMESTAMP\n" \ + >> $LOGFILE +echo "LOGFILE: All Events are Logged ==> $LOGFILE \n" > $TTY + +# Kill all functions +JOBS=$(jobs -p) +if [[ ! -z $JOBS && $JOBS != '' && $JOBS != '0' ]] +then + kill $(jobs -p) 2>/dev/null 1>&2 +fi +return 2 +} +#################################################### +pre_event_script () +{ +# Put anything that you want to execute BEFORE the +# monitored process STARTS in this function + +: # No-OP - Needed as a placeholder for an empty function + # Comment Out the Above colon, ':' + +PRE_RC=$? +return $PRE_RC +} +#################################################### +startup_event_script () +{ +# Put anything that you want to execute WHEN, or AS, the +# monitored process STARTS in this function + +: # No-OP - Needed as a placeholder for an empty function + # Comment Out the Above colon, ':' + +STARTUP_RC=$? +return $STARTUP_RC +} +#################################################### +post_event_script () +{ +# Put anything that you want to execute AFTER the +# monitored process ENDS in this function + +: # No-OP - Need as a placeholder for an empty function + # Comment Out the Above colon, ':' + +POST_RC=$? +return $POST_RC +} +#################################################### +# This function is used to test character strings + +test_string () +{ +if (( $# != 1 )) +then + echo 'ERROR' + return +fi + +C_STRING=$1 + +# Test the character string for its composition + +case $C_STRING in + + +([0-9])) echo 'POS_INT' # Integer >= 0 + ;; + +([-0-9])) echo 'NEG_INT' # Integer < 0 + ;; + +([a-z])) echo 'LOW_CASE' # lower case text + ;; + +([A-Z])) echo 'UP_CASE' # UPPER case text + ;; + +([a-z]|[A-Z])) echo 'MIX_CASE' # MIxed CAse text + ;; + *) echo 'UNKNOWN' # Anything else + ;; +esac +} +#################################################### +proc_watch () +{ +# set -x # Uncomment to debug this function +# This function does all of the process monitoring! + +while : # Loop Forever!! +do + case $RUN in + 'Y') + # This will run the startup_event_script, which is a function + if [[ $RUN_STARTUP_EVENT = 'Y' ]] + then + echo "STARTUP EVENT: Executing Startup Event Script..."\ + > $TTY + echo "STARTUP EVENT: Executing Startup Event Script..."\ + >> $LOGFILE + + startup_event_script # USER DEFINED FUNCTION!!! + RC=$? # Check the Return Code!! + if (( "RC" == 0 )) + then + echo "SUCCESS: Startup Event Script Completed RC - \ +${RC}" > $TTY + echo "SUCCESS: Startup Event Script Completed RC - \ +${RC}" >> $LOGFILE + + else + echo "FAILURE: Startup Event Script FAILED RC - \ +${RC}" > $TTY + echo "FAILURE: Startup Event Script FAILED RC - \ +${RC}" >> $LOGFILE + fi + fi + integer PROC_COUNT='-1' # Reset the Counters + integer LAST_COUNT='-1' + # Loop until the process(es) end(s) + + until (( "PROC_COUNT" == 0 )) + do + # This function is a Co-Process. $BREAK checks to see if + # "Program Interrupt" has taken place. If so BREAK will + # be 'Y' and we exit both the loop and function. + + read BREAK + if [[ $BREAK = 'Y' ]] + then + return 3 + fi + + PROC_COUNT=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME \ + | grep $PROCESS | wc -l) >/dev/null 2>&1 + + if (( "LAST_COUNT" > 0 && "LAST_COUNT" != "PROC_COUNT" )) + then + # The Process Count has Changed... + TIMESTAMP=$(date +%D@%T) + # Get a list of the PID of all of the processes + PID_LIST=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME \ + | grep $PROCESS | awk '{print $2}') + + echo "PROCESS COUNT: $PROC_COUNT $PROCESS\ +Processes Running ==> $TIMESTAMP" >> $LOGFILE & + echo "PROCESS COUNT: $PROC_COUNT $PROCESS\ +Processes Running ==> $TIMESTAMP" > $TTY + echo ACTIVE PIDS: $PID_LIST >> $LOGFILE & +echo ACTIVE PIDS: $PID_LIST > $TTY + fi + LAST_COUNT=$PROC_COUNT + sleep $INTERVAL # Needed to reduce CPU load! + done + + RUN='N' # Turn the RUN Flag Off + TIMESTAMP=$(date +%D@%T) + echo "ENDING PROCESS: $PROCESS END time ==>\ +$TIMESTAMP" >> $LOGFILE & + echo "ENDING PROCESS: $PROCESS END time ==>\ +$TIMESTAMP" > $TTY + + # This will run the post_event_script, which is a function + + if [[ $RUN_POST_EVENT = 'Y' ]] + then + echo "POST EVENT: Executing Post Event Script..."\ + > $TTY + echo "POST EVENT: Executing Post Event Script..."\ + >> $LOGFILE & + + post_event_script # USER DEFINED FUNCTION!!! + integer RC=$? + if (( "RC" == 0 )) + then + echo "SUCCESS: Post Event Script Completed RC - \ +${RC}" > $TTY + echo "SUCCESS: Post Event Script Completed RC - \ +${RC}" >> $LOGFILE + else + echo "FAILURE: Post Event Script FAILED RC - ${RC}"\ + > $TTY + echo "FAILURE: Post Event Script FAILED RC - ${RC}"\ + >> $LOGFILE + fi + fi + ;; + + 'N') + # This will run the pre_event_script, which is a function + + if [[ $RUN_PRE_EVENT = 'Y' ]] + then + echo "PRE EVENT: Executing Pre Event Script..." > $TTY + echo "PRE EVENT: Executing Pre Event Script..." >> $LOGFILE + + pre_event_script # USER DEFINED FUNCTION!!! + RC=$? # Check the Return Code!!! + if (( "RC" == 0 )) + then + echo "SUCCESS: Pre Event Script Completed RC - ${RC}"\ + > $TTY + echo "SUCCESS: Pre Event Script Completed RC - ${RC}"\ + >> $LOGFILE + else + echo "FAILURE: Pre Event Script FAILED RC - ${RC}"\ + > $TTY + echo "FAILURE: Pre Event Script FAILED RC - ${RC}"\ + >> $LOGFILE + fi + fi + + echo "WAITING: Waiting for $PROCESS to \ +startup...Monitoring..." + + integer PROC_COUNT='-1' # Initialize to a fake value + + # Loop until at least one process starts + + until (( "PROC_COUNT" > 0 )) + do + # This is a Co-Process. This checks to see if a "Program + # Interrupt" has taken place. If so BREAK will be 'Y' and + # we exit both the loop and function + + read BREAK + if [[ $BREAK = 'Y' ]] + then + return 3 + fi + + PROC_COUNT=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME | grep $PROCESS | wc -l) \ + >/dev/null 2>&1 + + sleep $INTERVAL # Needed to reduce CPU load! + done + + RUN='Y' # Turn the RUN Flag On + + TIMESTAMP=$(date +%D@%T) + + PID_LIST=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME \ + | grep $PROCESS | awk '{print $2}') + + if (( "PROC_COUNT" == 1 )) + then + echo "START PROCESS: $PROCESS START time ==>\ +$TIMESTAMP" >> $LOGFILE & + echo ACTIVE PIDS: $PID_LIST >> $LOGFILE & + echo "START PROCESS: $PROCESS START time ==>\ +$TIMESTAMP" > $TTY + echo ACTIVE PIDS: $PID_LIST > $TTY + elif (( "PROC_COUNT" > 1 )) + then + echo "START PROCESS: $PROC_COUNT $PROCESS\ +Processes Started: START time ==> $TIMESTAMP" >> $LOGFILE & + echo ACTIVE PIDS: $PID_LIST >> $LOGFILE & + echo "START PROCESS: $PROC_COUNT $PROCESS\ +Processes Started: START time ==> $TIMESTAMP" > $TTY + echo ACTIVE PIDS: $PID_LIST > $TTY + fi + ;; + esac +done +} + +#################################################### +############## START OF MAIN ####################### +#################################################### + +### SET A TRAP #### + +trap 'BREAK='Y';print -p $BREAK 2>/dev/null;trap_exit\ +2>/dev/null;exit 0' 1 2 3 15 + +BREAK='N' # The BREAK variable is used in the co-process proc_watch +PROCESS= # Initialize to null +integer TOTAL_SECONDS=0 + +# Check commnand line arguments + +if (( $# > 10 || $# < 2 )) +then + usage + exit 1 +fi + +# Check to see if only the seconds and a process are +# the only arguments + +if [[ ($# -eq 2) && ($1 != -*) && ($2 != -*) ]] +then + NUM_TEST=$(test_string $1) # Is this an Integer? + if [[ "$NUM_TEST" = 'POS_INT' ]] + then + TOTAL_SECONDS=$1 # Yep - It.s an Integer + PROCESS=$2 # Can be anything + else + usage + exit 1 + fi +else + # Since getopts does not care what arguments it gets, let.s + # do a quick sanity check to make sure that we only have + # between 2 and 10 arguments and the first one must start + # with a -* (hyphen and anything), else usage error + + case "$#" in + [2-10]) if [[ $1 != -* ]]; then + usage; exit 1 + fi + ;; + esac + + HOURS=0 # Initialize all to zero + MINUTES=0 + SECS=0 + DAYS=0 + + # Use getopts to parse the command line arguments + # For each $OPTARG for DAYS, HOURS, MINUTES and DAYS check to see + # that each one is an integer by using the check_string function + + while getopts ":h:H:m:M:s:S:d:D:P:p:" OPT_LIST 2>/dev/null + do + case $OPT_LIST in + h|H) [[ $(test_string $OPTARG) != 'POS_INT' ]] && usage && exit 1 + (( HOURS = $OPTARG * 3600 )) # 3600 seconds per hour + ;; + m|H) [[ $(test_string $OPTARG) != 'POS_INT' ]] && usage && exit 1 + (( MINUTES = $OPTARG * 60 )) # 60 seconds per minute + ;; + s|S) [[ $(test_string $OPTARG) != 'POS_INT' ]] && usage && exit 1 + SECS="$OPTARG" # seconds are seconds + ;; + d|D) [[ $(test_string $OPTARG) != 'POS_INT' ]] && usage && exit 1 + (( DAYS = $OPTARG * 86400 )) # 86400 seconds per day + ;; + p|P) PROCESS=$OPTARG # process can be anything + ;; + \?) usage # USAGE ERROR + exit 1 + ;; + :) usage + exit 1 + ;; + *) usage + exit 1 + ;; + esac + done +fi + +# We need to make sure that we have a process that +# is NOT null or empty! - sanity check - The double quotes are required! + +if [[ -z "$PROCESS" || "$PROCESS" = '' ]] +then + usage + exit 1 +fi + +# Check to see that TOTAL_SECONDS was not previously set + +if (( TOTAL_SECONDS == 0 )) +then + # Add everything together if anything is > 0 + if [[ $SECS -gt 0 || $MINUTES -gt 0 || $HOURS -gt 0 \ + || $DAYS -gt 0 ]] + then + (( TOTAL_SECONDS = SECS + MINUTES + HOURS + DAYS )) + fi +fi + +# Last Sanity Check! + +if (( TOTAL_SECONDS <= 0 )) || [ -z $PROCESS ] +then + # Either There are No Seconds to Count or the + # $PROCESS Variable is Null...USAGE ERROR... + + usage + exit 1 +fi + +########### START MONITORING HERE!########### + +echo "\nCurrently running $PROCESS processes:\n" > $TTY +ps aux | grep -v "grep $PROCESS" | grep -v $SCRIPT_NAME \ + | grep $PROCESS > $TTY + +PROC_RC=$? # Get the initial state of the monitored function + +echo >$TTY # Send a blank line to the screen + +(( PROC_RC != 0 )) && echo "\nThere are no $PROCESS processes running\n" + +if (( PROC_RC == 0 )) # The Target Process(es) is/are running... +then + RUN='Y' # Set the RUN flag to true, or yes. + + integer PROC_COUNT # Strips out the "padding" for display + + PROC_COUNT=$(ps aux | grep -v "grep $PROCESS" | grep -v \ + $SCRIPT_NAME | grep $PROCESS | wc -l) >/dev/null 2>&1 + if (( PROC_COUNT == 1 )) + then + echo "The $PROCESS process is currently\ +running...Monitoring...\n" + elif (( PROC_COUNT > 1 )) + then + print "There are $PROC_COUNT $PROCESS processes currently\ +running...Monitoring...\n" + fi +else + echo "The $PROCESS process is not currently running...monitoring..." + RUN='N' # Set the RUN flag to false, or no. +fi + +TIMESTAMP=$(date +%D@%T) # Time that this script started monitoring + +# Get a list of the currently active process IDs + +PID_LIST=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME \ + | grep $PROCESS | awk '{print $2}') + +echo "MON_STARTED: Monitoring for $PROCESS began ==> $TIMESTAMP" \ + | tee -a $LOGFILE +echo ACTIVE PIDS: $PID_LIST | tee -a $LOGFILE + +##### NOTICE #### +# We kick off the "proc_watch" function below as a "Co-Process" +# This sets up a two way communications link between the +# "proc_watch" background function and this "MAIN BODY" of +# the script. This is needed because the function has two +# "infinite loops", with one always executing at any given time. +# Therefore we need a way to break out of the loop in case of +# an interrupt, i.e. CTRL+C, and when the countdown is complete. +# The "pipe appersand", |&, creates the background Co-Process +# and we use "print -p $VARIABLE" to transfer the variable.s +# value back to the background co-process. +################################### + +proc_watch |& # Create a Background Co-Process!! +WATCH_PID=$! # Get the process ID of the last background job! + +# Start the Count Down! + +integer SECONDS_LEFT=$TOTAL_SECONDS + +while (( SECONDS_LEFT > 0 )) +do + # Next send the current value of $BREAK to the Co-Process + # proc_watch, which was piped to the background... + + print -p $BREAK 2>/dev/null + (( SECONDS_LEFT = SECONDS_LEFT - 1 )) + sleep 1 # 1 Second Between Counts +done + +# Finished - Normal Timeout Exit... +TIMESTAMP=$(date +%D@%T) # Get a new timestamp... +echo "MON_STOPPED: Monitoring for $PROCESS ended ==> $TIMESTAMP\n" \ + | tee -a $LOGFILE + +echo "LOGFILE: All Events are Logged ==> $LOGFILE \n" + +# Tell the proc_watch function to break out of the loop and die +BREAK='Y' +print -p $BREAK 2>/dev/null + +kill $WATCH_PID 2>/dev/null + +exit 0 + +# End of Script diff --git a/chapter11/elapsed_time b/chapter11/elapsed_time new file mode 100755 index 0000000..735a43e --- /dev/null +++ b/chapter11/elapsed_time @@ -0,0 +1,17 @@ +elapsed_time () +{ +# This function is intended for Bash shell scripts + +SEC=$1 + +(( SEC < 60 )) && echo -e "[Elasped time: \ +$SEC seconds]\c" + +(( SEC >= 60 && SEC < 3600 )) && echo -e \ +"[Elasped time: $(( SEC / 60 )) min $(( SEC % 60 )) sec]\c" + +(( SEC > 3600 )) && echo -e "[Elasped time: \ +$(( SEC / 3600 )) hr $(( (SEC % 3600) / 60 )) min \ +$(( (SEC % 3600) % 60 )) sec]\c" +} + diff --git a/chapter11/function_build_random_line b/chapter11/function_build_random_line new file mode 100755 index 0000000..083cea3 --- /dev/null +++ b/chapter11/function_build_random_line @@ -0,0 +1,19 @@ +build_random_line () +{ +# This function extracts random characters +# from the KEYS array by using the RANDOM +# shell variable + +C=1 +LINE= +until (( C > 79 )) +do + LINE="${LINE}${KEYS[$(($RANDOM % X + 1))]}" + (( C = C + 1 )) +done + +# Return the line of random characters + +echo "$LINE" +} + diff --git a/chapter11/function_get_date_time_stamp b/chapter11/function_get_date_time_stamp new file mode 100755 index 0000000..dc1d41a --- /dev/null +++ b/chapter11/function_get_date_time_stamp @@ -0,0 +1,6 @@ +function get_date_time_stamp +{ +DATE_STAMP=$(date +'%m%d%y.%H%M%S') +echo $DATE_STAMP +} + diff --git a/chapter11/function_get_random_number b/chapter11/function_get_random_number new file mode 100755 index 0000000..832bc3c --- /dev/null +++ b/chapter11/function_get_random_number @@ -0,0 +1,8 @@ +function get_random_number +{ +# This function gets the next random number from the +# $RANDOM variable. The range is 0 to 32767. + +echo "$RANDOM" +} + diff --git a/chapter11/function_get_second b/chapter11/function_get_second new file mode 100755 index 0000000..b1c15d0 --- /dev/null +++ b/chapter11/function_get_second @@ -0,0 +1,6 @@ +function get_second +{ +THIS_SECOND=$(date +%S) +echo $THIS_SECOND +} + diff --git a/chapter11/function_in_range_fixed_length_random_number_typeset b/chapter11/function_in_range_fixed_length_random_number_typeset new file mode 100755 index 0000000..5dd476d --- /dev/null +++ b/chapter11/function_in_range_fixed_length_random_number_typeset @@ -0,0 +1,27 @@ +function in_range_fixed_length_random_number_typeset +{ +# Create a pseudo-random number less than or equal +# to the $UPPER_LIMIT value, which is user defined. +# This function will also pad the output with leading +# zeros to keep the number of digits consistent using +# the typeset command. + +# Find the length of each character string + +UL_LENGTH=$(echo ${#UPPER_LIMIT}) + +# Fix the length of the RANDOM_NUMBER variable to +# the length of the UPPER_LIMIT variable, specified +# by the $UL_LENGTH variable. + +typeset -Z$UL_LENGTH RANDOM_NUMBER + +# Create a fixed length pseudo-random number + +RANDOM_NUMBER=$(($RANDOM % $UPPER_LIMIT + 1)) + +# Return the value of the fixed length $RANDOM_NUMBER + +echo $RANDOM_NUMBER +} + diff --git a/chapter11/function_in_range_random_number b/chapter11/function_in_range_random_number new file mode 100755 index 0000000..369404b --- /dev/null +++ b/chapter11/function_in_range_random_number @@ -0,0 +1,10 @@ +function in_range_random_number +{ +# Create a pseudo-random number less than or equal +# to the $UPPER_LIMIT value, which is user defined + +RANDOM_NUMBER=$(($RANDOM % $UPPER_LIMIT + 1)) + +echo "$RANDOM_NUMBER" +} + diff --git a/chapter11/function_load_default_keyboard b/chapter11/function_load_default_keyboard new file mode 100755 index 0000000..c774358 --- /dev/null +++ b/chapter11/function_load_default_keyboard @@ -0,0 +1,15 @@ +load_default_keyboard () +{ +# Loop through each character in the following list and +# append each character to the $CHAR_FILE file. This +# produces a file with one character on each line. + +for CHAR in 1 2 3 4 5 6 7 8 9 0 q w e r t y u i o \ + p a s d f g h j k l z x c v b n m \ + Q W E R T Y U I O P A S D F G H J K L \ + Z X C V B N M 0 1 2 3 4 5 6 7 8 9 +do + echo "$CHAR" >> $CHAR_FILE +done +} + diff --git a/chapter11/function_my_program b/chapter11/function_my_program new file mode 100755 index 0000000..b083e4d --- /dev/null +++ b/chapter11/function_my_program @@ -0,0 +1,11 @@ +function my_program +{ +# Put anything you want to process in this function. I +# recommend that you specify an external program of shell +# script to execute. + +echo "HELLO WORLD - $DATE_ST" > $UNIQUE_FN & + +# : # No-Op - Does nothing but has a return code of zero +} + diff --git a/chapter11/mk_unique_filename.ksh b/chapter11/mk_unique_filename.ksh new file mode 100755 index 0000000..757b963 --- /dev/null +++ b/chapter11/mk_unique_filename.ksh @@ -0,0 +1,168 @@ +#!/usr/bin/ksh +# +# AUTHOR: Randy Micahel +# SCRIPT: mk_unique_filename.ksh +# DATE: 11/12/2007 +# REV: 1.2.P +# +# PLATFORM: Not Platform Dependent +# +# EXIT CODES: +# 0 - Normal script execution +# 1 - Usage error +# +# REV LIST: +# +# +# set -x # Uncomment to debug +# set -n # Uncomment to debug without any execution +# +#################################################### +########## DEFINE FUNCTIONS HERE ################### +#################################################### + +function usage +{ +echo "\nUSAGE: $SCRIPT_NAME base_file_name\n" +} + +#################################################### + +function get_date_time_stamp +{ +DATE_STAMP=$(date +'%m%d%y.%H%M%S') +echo $DATE_STAMP +} + +#################################################### + +function get_second +{ +THIS_SECOND=$(date +%S) +echo $THIS_SECOND +} + +#################################################### + +function in_range_fixed_length_random_number_typeset +{ +# Create a pseudo-random number less than or equal +# to the $UPPER_LIMIT value, which is user defined. +# This function will also pad the output with leading +# zeros to keep the number of digits consistent using +# the typeset command. + +# Find the length of each character string + +UL_LENGTH=$(echo ${#UPPER_LIMIT}) + +# Fix the length of the RANDOM_NUMBER variable to +# the length of the UPPER_LIMIT variable, specified +# by the $UL_LENGTH variable. + +typeset -Z$UL_LENGTH RANDOM_NUMBER + +# Create a fixed length pseudo-random number + +RANDOM_NUMBER=$(($RANDOM % $UPPER_LIMIT + 1)) + +# Return the value of the fixed length $RANDOM_NUMBER + +echo $RANDOM_NUMBER +} + +#################################################### + +function my_program +{ +# Put anything you want to process in this function. I +# recommend that you specify an external program of shell +# script to execute. + +echo "HELLO WORLD - $DATE_ST" > $UNIQUE_FN & + +# : # No-Op - Does nothing but has a return code of zero +} + +#################################################### +################ BEGINNING OF MAIN ################# +#################################################### + +SCRIPT_NAME=$(basename $0) # Query the system for this script name + +# Check for the correct number of arguments - exactly 1 + +if (( $# != 1 )) +then + echo "\nERROR: Usage error...EXITING..." + usage + exit 1 +fi + +# What filename do we need to make unique? + +BASE_FN=$1 # Get the BASE filenaname to make unique + +RANDOM=$$ # Initialize the RANDOM environment variable + # with the current process ID (PID) + +UPPER_LIMIT=32767 # Set the UPPER_LIMIT + +CURRENT_SECOND=99 # Initialize to a non-second +LAST_SECOND=98 # Initialize to a non-second + +USED_NUMBERS= # Initialize to null + +PROCESSING="TRUE" # Initialize to run mode + +while [[ $PROCESSING = "TRUE" ]] +do + DATE_ST=$(get_date_time_stamp) # Get the current date/time + CURRENT_SECOND=$(get_second) # Get the current second + + RN=$(in_range_fixed_length_random_number_typeset) # Get a new number + + # Check to see if we have already used this number this second + + if (( CURRENT_SECOND == LAST_SECOND )) + then + UNIQUE=FALSE # Initialize to FALSE + while [[ "$UNIQUE" != "TRUE" ]] && [[ ! -z $UNIQUE ]] + do + # Has this number already been used this second? + echo $USED_NUMBERS | grep $RN >/dev/null 2>&1 + if (( $? == 0 )) + then + # Has been used...Get another number + RN=$(in_range_fixed_length_random_number_typeset) + else + # Number is unique this second... + UNIQUE=TRUE + # Add this numner to the used number list + USED_NUMBERS="$USED_NUMBERS $RN" + fi + done + else + USED_NUMBERS= # New second...Reinitialize to null + fi + # Assign the unique filename to the UNIQUE_FN variable + + UNIQUE_FN=${BASE_FN}.${DATE_ST}.$RN + + echo $UNIQUE_FN # Comment out this line!! + + LAST_SECOND=$CURRENT_SECOND # Save the last second value + + # We have a unique filename... + # + # PROCESS SOMETHING HERE AND REDIRECT OUTPUT TO $UNIQUE_FN + # + my_program + # + # IF PROCESSING IS FINISHED ASSIGN "FALSE" to the PROCESSING VARIABLE + # + # if [[ $MY_PROCESS = "done" ]] + # then + # PROCESSING="FALSE" + # fi +done diff --git a/chapter11/random_file.bash b/chapter11/random_file.bash new file mode 100755 index 0000000..8966eeb --- /dev/null +++ b/chapter11/random_file.bash @@ -0,0 +1,204 @@ +#!/bin/bash +# +# SCRIPT: random_file.bash +# AUTHOR: Randy Michael +# DATE: 8/3/2007 +# REV: 1.0 +# PLATFORM: Not Platform Dependent +# +# PURPOSE: This script is used to create a +# specific size file of random characters. +# The methods used in this script include +# loading an array with alphanumeric +# characters, then using the /dev/random +# character special file to seed the RANDOM +# shell variable, which in turn is used to +# extract a random set of characters from the +# KEYS array to build the OUTFILE of a +# specified MB size. +# +# set -x # Uncomment to debug this script +# +# set -n # Uncomment to check script syntax +# # without aby execution. Do not forget +# # to put the comment back in or the +# # script will never execute. +# +########################################## +# DEFINE FILES AND VARIABLES HERE +########################################## + +typeset -i MB_SIZE=$1 +typeset -i RN +typeset -i i=1 +typeset -i X=0 +WORKDIR=/scripts +OUTFILE=${WORKDIR}/largefile.random.txt +>$OUTFILE +THIS_SCRIPT=$(basename $0) +CHAR_FILE=${WORKDIR}/char_file.txt + +########################################## +# DEFINE FUNCTIONS HERE +########################################## + +build_random_line () +{ +# This function extracts random characters +# from the KEYS array by using the RANDOM +# shell variable + +C=1 +LINE= +until (( C > 79 )) +do + LINE="${LINE}${KEYS[$(($RANDOM % X + 1))]}" + (( C = C + 1 )) +done + +# Return the line of random characters + +echo "$LINE" +} + +########################################## + +elasped_time () +{ +SEC=$1 + +(( SEC < 60 )) && echo -e "[Elasped time: \ +$SEC seconds]\c" + +(( SEC >= 60 && SEC < 3600 )) && echo -e \ +"[Elasped time: $(( SEC / 60 )) min $(( SEC % 60 )) sec]\c" + +(( SEC > 3600 )) && echo -e "[Elasped time: \ +$(( SEC / 3600 )) hr $(( (SEC % 3600) / 60 )) min \ +$(( (SEC % 3600) % 60 )) sec]\c" +} + +########################################## + +load_default_keyboard () +{ +# Loop through each character in the following list and +# append each character to the $CHAR_FILE file. This +# produces a file with one character on each line. + +for CHAR in 1 2 3 4 5 6 7 8 9 0 q w e r t y u i o \ + p a s d f g h j k l z x c v b n m \ + Q W E R T Y U I O P A S D F G H J K L \ + Z X C V B N M 0 1 2 3 4 5 6 7 8 9 +do + echo "$CHAR" >> $CHAR_FILE +done +} +########################################## + +usage () +{ +echo -e "\nUSAGE: $THIS_SCRIPT Mb_size" +echo -e "Where Mb_size is the size of the file to build\n" +} + +########################################## +# BEGINNING OF MAIN +########################################## + +if (( $# != 1 )) +then + usage + exit 1 +fi + +# Test for an integer value + +case $MB_SIZE in +[0-9]) : # Do nothing + ;; + *) usage + ;; +esac + +# Test for the $CHAR_FILE + +if [ ! -s "$CHAR_FILE" ] +then + echo -e "\nNOTE: $CHAR_FILE does not esist" + echo "Loading default keyboard data." + echo -e "Creating $CHAR_FILE...\c" + load_default_keyboard + echo "Done" +fi + +# Load Character Array + +echo -e "\nLoading array with alphanumeric character elements" + +while read ARRAY_ELEMENT +do + (( X = X + 1 )) + KEYS[$X]=$ARRAY_ELEMENT + +done < $CHAR_FILE + +echo "Total Array Character Elements: $X" + +# Use /dev/random to seed the shell variable RANDOM + +echo "Querying the kernel random number generator for a random seed" + +RN=$(dd if=/dev/random count=1 2>/dev/null \ + | od -t u4 | awk '{print $2}'| head -n 1) + +# The shell variable RANDOM is limited to 32767 + +echo "Reducing the random seed value to between 1 and 32767" + +RN=$(( RN % 32767 + 1 )) + +# Initialize RANDOM with a new seed + +echo "Assigning a new seed to the RANDOM shell variable" + +RANDOM=$RN + +echo "Building a $MB_SIZE MB random character file ==> $OUTFILE" +echo "Please be patient, this may take some time to complete..." +echo -e "Executing: .\c" + +# Reset the shell SECONDS variable to zero seconds. + +SECONDS=0 + +TOT_LINES=$(( MB_SIZE * 12800 )) + +until (( i > TOT_LINES )) +do + build_random_line >> $OUTFILE + (( $(( i % 100 )) == 0 )) && echo -e ".\c" + (( i = i + 1 )) +done + +# Capture the total seconds + +TOT_SEC=$SECONDS + +echo -e "\n\nSUCCESS: $OUTFILE created at $MB_SIZE MB\n" + +elasped_time $TOT_SEC + +# Calculate the bytes/second file creation rate + +(( MB_SEC = ( MB_SIZE * 1024000 ) / TOT_SEC )) + +echo -e "\n\nFile Creation Rate: $MB_SEC bytes/second\n" + +echo -e "File size:\n" +ls -l $OUTFILE +echo + +########################################## +# END OF random_file.bash SCRIPT +########################################## diff --git a/chapter11/random_number.ksh b/chapter11/random_number.ksh new file mode 100755 index 0000000..2d77873 --- /dev/null +++ b/chapter11/random_number.ksh @@ -0,0 +1,150 @@ +#!/usr/bin/ksh +# +# AUTHOR: Randy Micahel +# SCRIPT: random_number.ksh +# DATE: 11/12/2007 +# REV: 1.2.P +# +# PLATFORM: Not Platform Dependent +# +# EXIT CODES: +# 0 - Normal script execution +# 1 - Usage error +# +# REV LIST: +# +# +# set -x # Uncomment to debug +# set -n # Uncomment to check syntax without any command execution +# +#################################################### +########## DEFINE FUNCTIONS HERE ################### +#################################################### + +function usage +{ +echo "\nUSAGE: $SCRIPT_NAME [-f] [upper_number_range]" +echo "\nEXAMPLE: $SCRIPT_NAME" +echo "Will return a random number between 0 and 32767" +echo "\nEXAMPLE: $SCRIPT_NAME 1000" +echo "Will return a random number between 1 and 1000" +echo "\nEXAMPLE: $SCRIPT_NAME -f 1000" +echo "Will add leading zeros to a random number from" +echo "1 to 1000, which keeps the number of digits consistant\n" +} + +#################################################### + +function get_random_number +{ +# This function gets the next random number from the +# $RANDOM variable. The range is 0 to 32767. + +echo "$RANDOM" +} + +#################################################### + +function in_range_random_number +{ +# Create a pseudo-random number less than or equal +# to the $UPPER_LIMIT value, which is user defined + +RANDOM_NUMBER=$(($RANDOM % $UPPER_LIMIT + 1)) + +echo "$RANDOM_NUMBER" +} + +#################################################### + +function in_range_fixed_length_random_number_typeset +{ +# Create a pseudo-random number less than or equal +# to the $UPPER_LIMIT value, which is user defined. +# This function will also pad the output with leading +# zeros to keep the number of digits consistent using +# the typeset command. + +# Find the length of each character string + +UL_LENGTH=$(echo ${#UPPER_LIMIT}) + +# Fix the length of the RANDOM_NUMBER variable to +# the length of the UPPER_LIMIT variable, specified +# by the $UL_LENGTH variable. + +typeset -Z$UL_LENGTH RANDOM_NUMBER + +# Create a fixed length pseudo-random number + +RANDOM_NUMBER=$(($RANDOM % $UPPER_LIMIT + 1)) + +# Return the value of the fixed length $RANDOM_NUMBER + +echo $RANDOM_NUMBER +} + +#################################################### +############## BEGINNING OF MAIN ################### +#################################################### + +SCRIPT_NAME=`basename $0` + +RANDOM=$$ # Initialize the RANDOM environment variable + # using the PID as the initial seed + +case $# in +0) get_random_number +;; + +1) UPPER_LIMIT="$1" + + # Test to see if $UPPER_LIMIT is a number + + case $UPPER_LIMIT in + +([0-9])) : # Do Nothing...It's a number + # NOTE: A colon (:) is a no-op in Korn shell + ;; + *) echo "\nERROR: $UPPER_LIMIT is not a number..." + usage + exit 1 + ;; + esac + + in_range_random_number +;; + +2) # Check for the -f switch to fix the length. + + if [[ $1 = '-f' ]] || [[ $1 = '-F' ]] + then + + UPPER_LIMIT="$2" + + # Test to see if $UPPER_LIMIT is a number + + case $UPPER_LIMIT in + +([0-9])) : # Do Nothing...It's a number + # NOTE: A colon (:) is a no-op in Korn shell + ;; + *) echo "\nERROR: $UPPER_LIMIT is not a number..." + usage + exit 1 + ;; + esac + + in_range_fixed_length_random_number_typeset + + else + echo "\nInvalid argument $1, see usage below..." + usage + exit 1 + fi +;; + +*) usage + exit 1 +;; +esac + +# End of random_number.ksh Shell Script diff --git a/chapter11/random_number_testing.bash b/chapter11/random_number_testing.bash new file mode 100755 index 0000000..14f84a7 --- /dev/null +++ b/chapter11/random_number_testing.bash @@ -0,0 +1,22 @@ +#!/bin/bash +# +# SCRIPT: random_number_testing.bash +# AUTHOR: Randy Michael +# DATE: 8/8/2007 + + +# For each 1K count we ger 64 lines of random numbers + +dd if=/dev/urandom count=1k bs=1 2>/dev/null | od -t u2 \ + | awk '{print $2}' | sed /^$/d > 64random_numbers.txt + +# For each 1M count we get 65536 lines of random numbers + +dd if=/dev/urandom count=1M bs=1 2>/dev/null | od -t u2 \ + | awk '{print $2}' | sed /^$/d > 65536random_numbers.txt + +# The opposite is /dev/zero which will create a null +# file of a specific size + +dd if=/dev/zero of=1MBemptyfile count=1M bs=1 2>/dev/null + diff --git a/chapter12/function_build_manager_password_report b/chapter12/function_build_manager_password_report new file mode 100755 index 0000000..5678435 --- /dev/null +++ b/chapter12/function_build_manager_password_report @@ -0,0 +1,36 @@ +function build_manager_password_report +{ +# Build a file to print for the secure envelope +( +$ECHO "\n RESTRICTED USE!!!" +$ECHO "\n\n\tImmediately send an e-mail to:\n" + +$ECHO " $NOTIFICATION_LIST" + +$ECHO "\n\tif this password is revealed!" +$ECHO "\n\tAIX root password: $PW\n" + +$ECHO "\n\n" + +$ECHO "\n RESTRICTED USE!!!" +$ECHO "\n\n\tImmediately send an e-mail to:\n" + +$ECHO " $NOTIFICATION_LIST" + +$ECHO "\n\tif this password is revealed!" +$ECHO "\n\tAIX root password: $PW\n" + +$ECHO "\n\n" + +$ECHO "\n RESTRICTED USE!!!" +$ECHO "\n\n\tImmediately send an e-mail to:\n" + +$ECHO " $NOTIFICATION_LIST" + +$ECHO "\n\tif this password is revealed!" +$ECHO "\n\tAIX root password: $PW\n" + + ) > $OUTFILE + +} + diff --git a/chapter12/function_check_for_and_create_keyboard_file b/chapter12/function_check_for_and_create_keyboard_file new file mode 100755 index 0000000..038d16e --- /dev/null +++ b/chapter12/function_check_for_and_create_keyboard_file @@ -0,0 +1,32 @@ +function check_for_and_create_keyboard_file +{ +# If the $KEYBOARD_FILE does not exist then +# ask the user to load the "standard" keyboard +# layout, which is done with the load_default_keyboard +# function. + +if [ ! -s $KEYBOARD_FILE ] +then + $ECHO "\n\nERROR: Missing Keyboard File" + $ECHO "\n\nWould You Like to Load the" + $ECHO "Default Keyboard Layout?" + $ECHO "\n\t(Y/N): \c" + typeset -u REPLY=FALSE + read REPLY + if [ $REPLY != Y ] + then + $ECHO "\n\nERROR: This shell script cannot operate" + $ECHO "without a keyboard data file located in" + $ECHO "\n==> $KEYBOARD_FILE\n" + $ECHO "\nThis file expects one character per line." + $ECHO "\n\t...EXITING...\n" + exit 3 + else + load_default_keyboard + $ECHO "\nPress ENTER when you are you ready to continue: \c" + read REPLY + clear + fi +fi +} + diff --git a/chapter12/function_in_range_random_number b/chapter12/function_in_range_random_number new file mode 100755 index 0000000..fdffc28 --- /dev/null +++ b/chapter12/function_in_range_random_number @@ -0,0 +1,14 @@ +function in_range_random_number +{ +# Create a pseudo-random number less than or equal +# to the $UPPER_LIMIT value, which is defined in the +# main body of the shell script. + +RN=$(dd if=/dev/urandom count=1 2>/dev/null \ + | od -t u2 | awk '{print $2}'| head -n 1) + +RANDOM_NUMBER=$(( RN % UPPER_LIMIT + 1)) + +echo "$RANDOM_NUMBER" +} + diff --git a/chapter12/function_load_default_keyboard b/chapter12/function_load_default_keyboard new file mode 100755 index 0000000..8d04b90 --- /dev/null +++ b/chapter12/function_load_default_keyboard @@ -0,0 +1,42 @@ +function load_default_keyboard +{ +# If a keyboard data file does not exist then the user +# prompted to load the standard keyboard data into the +# $KEYBOARD_FILE, which is defined in the main body of +# the shell script. + +clear # Clear the screen + +$ECHO "\nLoad the default keyboard data file? (Y/N): \c" +read REPLY + +case $REPLY in +y|Y) : + ;; + *) $ECHO "\nSkipping the load of the default keyboard file...\n" + return + ;; +esac + +cat /dev/null > $KEYBOARD_FILE + +$ECHO "\nLoading the Standard Keyboard File...\c" + +# Loop through each character in the following list and +# append each character to the $KEYBOARD_FILE file. This +# produces a file with one character on each line. + +for CHAR in \` 1 2 3 4 5 6 7 8 9 0 - = \\ q w e r t y u i o \ + p \[ \] a s d f g h j k l \; \' z x c v b n m \, \ + \. \/ \\ \~ \! \@ \# \$ \% \^ \& \* \( \) _ \+ \| \ + Q W E R T Y U I O P \{ \} A S D F G H J K L \: \" \ + Z X C V B N M \< \> \? \| \. 0 1 2 3 4 5 6 7 8 9 \/ \ + \* \- \+ +do + $ECHO "$CHAR" >> $KEYBOARD_FILE +done +$ECHO "\n\n\t...Done...\n" + +sleep 1 +} + diff --git a/chapter12/mk_passwd.ksh b/chapter12/mk_passwd.ksh new file mode 100755 index 0000000..f750662 --- /dev/null +++ b/chapter12/mk_passwd.ksh @@ -0,0 +1,463 @@ +#!/bin/ksh +# +# AUTHOR: Randy Micahel +# SCRIPT: mk_passwd.ksh +# DATE: 11/12/2007 +# REV: 2.5.P +# +# PLATFORM: Not Platform Dependent +# +# PURPOSE: This script is used to create pseudo-random passwords. +# An extrernal keyboard data file is utilized, which is +# defined by the KEYBOARD_FILE variable. This keyboard +# file is expected to have one character on each line. +# These characters are loaded into an array, and using +# pseudo-random numbers generated, the characters are +# "randomly" put together to form a string of characters. +# By default, this script produces 8 character passwords, +# but this length can be changed on the command line by +# adding an integer value after the script name. There are +# two command line options, -n, which creates the default +# KEYBOARD_FILE, and -m, which prints the manager's +# password report. This password report is intended +# to be lock in a safe for safe keeping. +# +# EXIT CODES: +# 0 - Normal script execution +# 1 - Usage error +# 2 - Trap exit +# 3 - Missing Keyboard data file +# 4 - $DEFAULT_PRINTER is NULL +# 5 - /dev/urandom file does not exist +# +# REV LIST: +# 6/26/2007: Added two command line options, -n, which +# creates a new $KEYBOARD_FILE, and -m, which prints +# the managers password report. +# +# 8/8/2007: Changed the random number method from the +# shell RANDOM variable to now use the /dev/urandom +# character special file in concert with dd and od. +# +# set -x # Uncomment to debug +# set -n # Uncomment to check syntax without any command execution +# +#################################################### +########### DEFINE SOME VARIABLES HERE ############# +#################################################### + +LENGTH=8 # Default Password Length + +# Notification List for Printing the Manager's +# Password Report for Locking Away Passwords +# Just in Case You are Unavaliable. + +NOTIFICATION_LIST="Donald Duck, Yogi Bear, and Mr. Ranger" + +# Define the Default Printer for Printing the Manager's +# Password Report. The user has a chance to change this +# printer at execution time. + +DEFAULT_PRINTER="hp4@yogi" + +SCRIPT=$(basename $0) + +OUTFILE="/tmp/tmppdw.file" + +KEYBOARD_FILE=/scripts/keyboard.keys + +PRINT_PASSWORD_MANAGER_REPORT="TO_BE_SET" + +# This next test ensures we use the correct +# echo command based on the executing shell + +ECHO="echo -e" +[[ $(basename $SHELL) = ksh ]] && ECHO=echo + +#################################################### +########## DEFINE FUNCTIONS HERE ################### +#################################################### +# +function in_range_random_number +{ +# Create a pseudo-random number less than or equal +# to the $UPPER_LIMIT value, which is defined in the +# main body of the shell script. + +RN=$(dd if=/dev/urandom count=1 2>/dev/null \ + | od -t u2 | awk '{print $2}'| head -n 1) + +RANDOM_NUMBER=$(( RN % UPPER_LIMIT + 1)) + +echo "$RANDOM_NUMBER" +} +# +#################################################### +# +function load_default_keyboard +{ +# If a keyboard data file does not exist then the user +# prompted to load the standard keyboard data into the +# $KEYBOARD_FILE, which is defined in the main body of +# the shell script. + +clear # Clear the screen + +$ECHO "\nLoad the default keyboard data file? (Y/N): \c" +read REPLY + +case $REPLY in +y|Y) : + ;; + *) $ECHO "\nSkipping the load of the default keyboard file...\n" + return + ;; +esac + +cat /dev/null > $KEYBOARD_FILE + +$ECHO "\nLoading the Standard Keyboard File...\c" + +# Loop through each character in the following list and +# append each character to the $KEYBOARD_FILE file. This +# produces a file with one character on each line. + +for CHAR in \` 1 2 3 4 5 6 7 8 9 0 - = \\ q w e r t y u i o \ + p \[ \] a s d f g h j k l \; \' z x c v b n m \, \ + \. \/ \\ \~ \! \@ \# \$ \% \^ \& \* \( \) _ \+ \| \ + Q W E R T Y U I O P \{ \} A S D F G H J K L \: \" \ + Z X C V B N M \< \> \? \| \. 0 1 2 3 4 5 6 7 8 9 \/ \ + \* \- \+ +do + $ECHO "$CHAR" >> $KEYBOARD_FILE +done +$ECHO "\n\n\t...Done...\n" + +sleep 1 +} +# +#################################################### +# +function check_for_and_create_keyboard_file +{ +# If the $KEYBOARD_FILE does not exist then +# ask the user to load the "standard" keyboard +# layout, which is done with the load_default_keyboard +# function. + +if [ ! -s $KEYBOARD_FILE ] +then + $ECHO "\n\nERROR: Missing Keyboard File" + $ECHO "\n\nWould You Like to Load the" + $ECHO "Default Keyboard Layout?" + $ECHO "\n\t(Y/N): \c" + typeset -u REPLY=FALSE + read REPLY + if [ $REPLY != Y ] + then + $ECHO "\n\nERROR: This shell script cannot operate" + $ECHO "without a keyboard data file located in" + $ECHO "\n==> $KEYBOARD_FILE\n" + $ECHO "\nThis file expects one character per line." + $ECHO "\n\t...EXITING...\n" + exit 3 + else + load_default_keyboard + $ECHO "\nPress ENTER when you are you ready to continue: \c" + read REPLY + clear + fi +fi +} +# +#################################################### +# +function build_manager_password_report +{ +# Build a file to print for the secure envelope +( +$ECHO "\n RESTRICTED USE!!!" +$ECHO "\n\n\tImmediately send an e-mail to:\n" + +$ECHO " $NOTIFICATION_LIST" + +$ECHO "\n\tif this password is revealed!" +$ECHO "\n\tAIX root password: $PW\n" + +$ECHO "\n\n" + +$ECHO "\n RESTRICTED USE!!!" +$ECHO "\n\n\tImmediately send an e-mail to:\n" + +$ECHO " $NOTIFICATION_LIST" + +$ECHO "\n\tif this password is revealed!" +$ECHO "\n\tAIX root password: $PW\n" + +$ECHO "\n\n" + +$ECHO "\n RESTRICTED USE!!!" +$ECHO "\n\n\tImmediately send an e-mail to:\n" + +$ECHO " $NOTIFICATION_LIST" + +$ECHO "\n\tif this password is revealed!" +$ECHO "\n\tAIX root password: $PW\n" + + ) > $OUTFILE + +} +# +#################################################### +# +function usage +{ +$ECHO "\nUSAGE: $SCRIPT [-m] [-n] [password_length]\n" +$ECHO " Where: + + -m Creates a password printout for Security + + -n Loads the default keyboard data keys file + + password_length - Interger value that overrides + the default 8 character + password length.\n" +} +# +#################################################### +# +function trap_exit +{ +rm -f $OUTFILE >/dev/null 2>&1 +} + +#################################################### +########## END OF FUNCTION DEFINITIONS ############# +#################################################### + +#################################################### +########## ENSURE /dev/urandom EXISTS ############## +#################################################### + +if ! [ -c /dev/urandom ] +then + echo "\nERROR: This version of $(uname) does not have a /dev/urandom +character special file. This script requires this file to create +pseudo random numbers." + echo "\n...EXITING...\n" + usage + exit 5 +fi + +#################################################### +####### VALIDATE EACH COMMAND LINE ARGUMENT ######## +#################################################### + +# Check command line arguments - $# < 3 + +if (($# > 3)) +then + usage + exit 1 +fi + +#################################################### +# +# Test for valid command line arguments - +# Valid auguments are "-n, -N, -m, -M, and any integer + +if (($# != 0)) +then + for CMD_ARG in $@ + do + case $CMD_ARG in + +([-0-9])) + # The '+([-0-9]))' test notation is looking for + # an integer. Any integer is assigned to the + # length of password variable, LENGTH + + LENGTH=$CMD_ARG + ;; + -n) : + ;; + -N) : + ;; + -m) : + ;; + -M) : + ;; + *) + usage + exit 1 + ;; + esac + done +fi + +#################################################### +# +# Ensure that the $LENGTH variable is an integer + +case $LENGTH in ++([0-9])) : # The '+([-0]))' test notation is looking for + # an integer. If an integer then the + # No-Op, specified by a colon, (Do Nothina)i + # command is executed, otherwise this script + # exits with a return code of 1, one. + ;; +*) usage + exit 1 + ;; +esac + +#################################################### +# +# Use the getopts function to parse the command +# line arguments. + +while getopts ":nNmM" ARGUMENT 2>/dev/null +do + case $ARGUMENT in + n|N) + # Create a new Keyboard Data file + load_default_keyboard + $ECHO "\nPress ENTER when you are you ready to continue: \c" + read REPLY + clear + ;; + m|M) + # Print the Manager Password Report + PRINT_PASSWORD_MANAGER_REPORT=TRUE + ;; + \?) # Show the usage message + usage + exit 1 + esac +done + +#################################################### +################ START OF MAIN ##################### +#################################################### + +# Set a trap + +trap 'trap_exit;exit 2' 1 2 3 15 + +#################################################### +# +# Check for a keyboard data file + +check_for_and_create_keyboard_file + +#################################################### +############### LOAD THE ARRAY ##################### +#################################################### + +X=0 # Initialize the array counter to zero + +# Load the array called "KEYS" with keyboard elements +# located in the $KEYBOARD_FILE. + +while read ARRAY_ELEMENT +do + ((X = X + 1)) # Increment the counter by 1 + + # Load an array element in the the array + + KEYS[$X]=$ARRAY_ELEMENT + +done < $KEYBOARD_FILE + + +UPPER_LIMIT=$X # Random Number Upper Limit + +#################################################### +# +# Create the pseudo-random password in this section + + + +clear # Clear the screen + +PW= # Initialize the password to NULL + +# Build the password using random numbers to grab array +# elements from the KEYS array. + +X=0 +while ((X < LENGTH)) +do + # Increment the password length counter + (( X = X + 1 )) + + # Build the password one char at a time in this loop + PW=${PW}${KEYS[$(in_range_random_number $UPPER_LIMIT)]} +done + +# Done building the password + +#################################################### +# +# Display the new pseudo-random password to the screen + +$ECHO "\n\n The new $LENGTH character password is:\n" +$ECHO "\n ${PW}\n" + +#################################################### +# +# Print the Manager's password report, if specified +# on the command with the -m command switch. + +if [ $PRINT_PASSWORD_MANAGER_REPORT = TRUE ] +then + + typeset -u REPLY=N + + $ECHO "\nPrint Password Sheet for the Secure Envelope? (Y/N)? \c" + read REPLY + + if [[ $REPLY = 'Y' ]] + then + build_manager_password_report + + REPLY= # Set REPLY to NULL + + $ECHO "\nPrint to the Default Printer ${DEFAULT_PRINTER} (Y/N)? \c" + read REPLY + if [[ $REPLY = 'Y' ]] + then + $ECHO "\nPrinting to $DEFAULT_PRINTER\n" + lp -c -d $DEFAULT_PRINTER $OUTFILE + + else + $ECHO "\nNEW PRINT QUEUE: \c" + read DEFAULT_PRINTER + if [ -z "$DEFAULT_PRINTER" ] + then + echo "ERROR: Default printer canot be NULL...Exiting..." + exit 5 + fi + $ECHO "\nPrinting to $DEFAULT_PRINTER\n" + lp -c -d $DEFAULT_PRINTER $OUTFILE + fi + else + $ECHO "\n\n\tO.K. - Printing Skipped..." + fi +fi + +#################################################### +# +# Remove the $OUTFILE, if it exists and has a size +# greater than zero bytes. + +[ -s $OUTFILE ] && rm $OUTFILE + +#################################################### +# +# Clear the screen and exit + +$ECHO "\n\nPress ENTER to Clear the Screen and EXIT: \c" +read X +clear + +# End of mk_passwd.ksh shell script diff --git a/chapter13/float_add.ksh b/chapter13/float_add.ksh new file mode 100755 index 0000000..e3f5b35 --- /dev/null +++ b/chapter13/float_add.ksh @@ -0,0 +1,234 @@ +#!/usr/bin/ksh +# +# SCRIPT: float_add.ksh +# AUTHOR: Randy Michael +# DATE: 03/01/2007 +# REV: 1.1.A +# +# PURPOSE: This shell script is used to add a list of numbers +# together. The numbers can be either integers or floating- +# point numbers. For floating-point numbers the user has +# the option of specifying a scale of the number of digits to +# the right of the decimal point. The scale is set by adding +# a -s or -S followed by an integer number. +# +# EXIT CODES: +# 0 ==> This script completed without error +# 1 ==> Usage error +# 2 ==> This script exited on a trapped signal +# +# REV. LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to debug without any command execution +# +######################################################## +############## DEFINE VARIABLE HERE #################### +######################################################## + +SCRIPT_NAME=$(basename $0) # The name of this shell script +SCALE="0" # Initialize the scale value to zero +NUM_LIST= # Initialize the NUM_LIST variable to NULL +COUNT=0 # Initialize the counter to zero +MAX_COUNT=$# # Set MAX_COUNT to the total number of + # command-line arguments. + +######################################################## +################ FUNCTIONS ############################# +######################################################## + +function usage +{ +echo "\nPURPOSE: Adds a list of numbers together\n" +echo "USAGE: $SCRIPT_NAME [-s scale_value] N1 N2...Nn" +echo "\nFor an integer result without any significant decimal places..." +echo "\nEXAMPLE: $SCRIPT_NAME 2048.221 65536 \n" +echo "OR for 4 significant decimal places" +echo "\nEXAMPLE: $SCRIPT_NAME -s 4 8.09838 2048 65536 42.632" +echo "\n\t...EXITING...\n" +} + +######################################################## + +function exit_trap +{ +echo "\n...EXITING on trapped signal...\n" +} + +######################################################## +################# START OF MAIN ######################## +######################################################## + +###### Set a Trap ###### + +trap 'exit_trap; exit 2' 1 2 3 15 + +######################################################## + +# Check for at least two command-line arguments + +if [ $# -lt 2 ] +then + echo "\nERROR: Please provide a list of numbers to add" + usage + exit 1 +fi + +# Parse the command-line arguments to find the scale value, if present. + +while getopts ":s:S:" ARGUMENT +do + case $ARGUMENT in + s|S) SCALE=$OPTARG + ;; + \?) # Because we may have negative numbers we need + # to test to see if the ARGUMENT that begins with a + # hyphen (-) is a number, and not an invalid switch!!! + + for TST_ARG in $* + do + if [[ $(echo $TST_ARG | cut -c1) = '-' ]] \ + && [ $TST_ARG != '-s' -a $TST_ARG != '-S' ] + then + case $TST_ARG in + +([-0-9])) : # No-op, do nothing + ;; + +([-0-9].[0-9])) + : # No-op, do nothing + ;; + +([-.0-9])) : # No-op, do nothing + ;; + *) echo "\nERROR: Invalid argument \ +on the command line" + usage + exit 1 + ;; + esac + fi + done + ;; + esac +done + +######################################################## + +# Parse through the command-line arguments and gather a list +# of numbers to add together and test each value. + +while ((COUNT < MAX_COUNT)) +do + ((COUNT = COUNT + 1)) + TOKEN=$1 # Grab a command-line argument on each loop iteration + case $TOKEN in # Test each value and look for a scale value. + -s|-S) shift 2 + ((COUNT = COUNT + 1)) + ;; + -s${SCALE}) shift + ;; + -S${SCALE}) shift + ;; + *) # Add the number ($TOKEN) to the list + NUM_LIST="${NUM_LIST} $TOKEN" + ((COUNT < MAX_COUNT)) && shift + ;; + esac +done + +######################################################## + +# Ensure that the scale is an integer value + +case $SCALE in + # Test for an integer + +([0-9])) : # No-Op - Do Nothing + ;; + *) echo "\nERROR: Invalid scale - $SCALE - \ +Must be an integer" + usage + exit 1 + ;; +esac + +######################################################## + +# Check each number supplied to ensure that the "numbers" +# are either integers or floating-point numbers. + +for NUM in $NUM_LIST +do + case $NUM in + +([0-9])) # Check for an integer + : # No-op, do nothing. + ;; + +([-0-9])) # Check for a negative whole number + : # No-op, do nothing + ;; + +([0-9]|[.][0-9])) + # Check for a positive floating-point number + : # No-op, do nothing + ;; + +(+[0-9][.][0-9])) + # Check for a positive floating-point number + # with a + prefix + : # No-op, do nothing + ;; + +(-[0-9][.][0-9])) + # Check for a negative floating-point number + : # No-op, do nothing + ;; + +([-.0-9])) + # Check for a negative floating-point number + : # No-op, do nothing + ;; + +([+.0-9])) + # Check for a positive floating-point number + : # No-op, do nothing + ;; + *) echo "\nERROR: $NUM is NOT a valid number" + usage + exit 1 + ;; + esac +done + +######################################################## + +# Build the list of numbers to add + +ADD= # Initialize the ADD variable to NULL +PLUS= # Initialize the PLUS variable to NULL + +# Loop through each number and build a math statement that +# will add all of the numbers together. + +for X in $NUM_LIST +do + # If the number has a + prefix, remove it! + if [[ $(echo $X | cut -c1) = '+' ]] + then + X=$(echo $X | cut -c2-) + fi + ADD="$ADD $PLUS $X" + PLUS="+" +done + +######################################################## +# Do the math here by using a here document to supply +# input to the bc command. The sum of the numbers is +# assigned to the SUM variable. + +echo "\nSCALE is $SCALE\n" + + +SUM=$(bc < This script completed without error +# 1 ==> Usage error +# 2 ==> This script exited on a trapped signal +# +# REV. LIST: +# +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to debug without any command execution + +SCRIPT_NAME=`basename $0` +ARG1="$1" +ARG2="$2" +ARG_LIST="$*" +TOTAL_TOKENS="$#" +SCALE="0" + +######################################################## +################ FUNCTIONS ############################# +######################################################## + +function usage +{ +echo "\n\n" +echo "Numbers to average...\n" +echo "USAGE: $SCRIPT_NAME [-s scale_value] N1 N2...N#" +echo "\nFor an integer result without any significant decimal places..." +echo "\nEXAMPLE: $SCRIPT_NAME 2048.221 65536 \n" +echo "OR for 4 significant decimal places" +echo "\nEXAMPLE: $SCRIPT_NAME -s 4 8.09838 2048 65536 42.632\n" +echo "Try again...EXITING...\n" +} +######################################################## + +function exit_trap +{ +echo "\n...EXITING on trapped signal...\n" +} + +######################################################## +################ START OF MAIN ######################### +######################################################## + +###### Set a Trap ###### + +trap 'exit_trap; exit 2' 1 2 3 15 + +######################## + +if [ $# -lt 2 ] +then + echo "\nIncorrect number of command auguments...Nothing to average..." + usage + exit 1 +fi + +if [ $ARG1 = "-s" ] || [ $ARG1 = "-S" ] +then + echo $ARG2 | grep [[:digit:]] >/dev/null 2>&1 + if [ $? -ne "0" ] + then + echo "\nERROR: Invalid argument - $ARG2 ... Must be an integer...\n" + usage + exit 1 + fi + echo $ARG2 | grep "\." >/dev/null 2>&1 + if [ $? -ne "0" ] + then + SCALE="$ARG2" + NUM_LIST=`echo $ARG_LIST | cut -f 3- -d " "` + (( TOTAL_TOKENS = $TOTAL_TOKENS - 2 )) + else + echo "\nERROR: Invalid scale - $ARG2 ... Scale must be an integer...\n" + usage + exit 1 + fi +else + NUM_LIST=$ARG_LIST +fi + +for TOKEN in $NUM_LIST +do + echo $TOKEN | grep [[:digit:]] >/dev/null 2>&1 + RC=$? + case $RC in + 0) cat /dev/null + ;; + *) echo "\n$TOKEN is not a number...Invalid argument list" + usage + exit 1 + ;; + esac +done + + +# Build the list of numbers to add for averaging... + +ADD="" +PLUS="" +for X in $NUM_LIST +do + ADD="$ADD $PLUS $X" + PLUS="+" +done + +# Do the math here + +AVERAGE=`bc < This script exited normally +# 1 ==> Usage or syntax error +# 2 ==> This script exited on a trapped signal +# +# REV. LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to debug without any command execution +# +######################################################## +############## DEFINE VARIABLE HERE #################### +######################################################## + +SCRIPT_NAME=`basename $0` +SCALE="0" # Initialize the scale value to zero +NUM_LIST= # Initialize the NUM_LIST to NULL +COUNT=0 # Initialize the counter to zero +MAX_COUNT=$# # Set MAX_COUNT to the total number of + # command-line arguments + +######################################################## +################ FUNCTIONS ############################# +######################################################## + +function usage +{ +echo "\nPURPOSE: Divides two numbers\n" +echo "USAGE: $SCRIPT_NAME [-s scale_value] N1 N2" +echo "\nFor an integer result without any significant decimal places..." +echo "\nEXAMPLE: $SCRIPT_NAME 2048.221 65536 \n" +echo "OR for 4 significant decimal places" +echo "\nEXAMPLE: $SCRIPT_NAME -s 4 2048.221 65536" +echo "\n\t...EXITING...\n" +} + +######################################################## + +function exit_trap +{ +echo "\n...EXITING on trapped signal...\n" +} + +######################################################## +################ START OF MAIN ######################### +######################################################## + +###### Set a Trap ###### + +trap 'exit_trap; exit 2' 1 2 3 15 + +######################## + +# Check for at least two command-line arguments +# and not more than four + +if (($# < 2)) +then + echo "\nERROR: Too few command line arguments" + usage + exit 1 +elif (($# > 4)) +then + echo "\nERROR: Too many command line arguments" + usage + exit 1 +fi + +# Parse the command-line arguments to find the scale value, if present. + +while getopts ":s:S:" ARGUMENT +do + case $ARGUMENT in + s|S) SCALE=$OPTARG + ;; + \?) # Because we may have negative numbers we need + # to test to see if the ARGUMENT that begins with a + # hyphen (-) is a number, and not an invalid switch!!! + + for TST_ARG in $* + do + if [[ $(echo $TST_ARG | cut -c1) = '-' ]] \ + && [ $TST_ARG != '-s' -a $TST_ARG != '-S' ] + then + case $TST_ARG in + +([-0-9])) : # No-op, do nothing + ;; + +([-0-9].[0-9])) + : # No-op, do nothing + ;; + +([-.0-9])) : # No-op, do nothing + ;; + *) echo "\nERROR: $TST_ARG is an invalid argument\n" + usage + exit 1 + ;; + esac + fi + done + ;; + esac +done + +######################################################## + +# Parse through the command-line arguments and gather a list +# of numbers to divide. + +TOTAL_NUMBERS=0 + +while ((COUNT < MAX_COUNT)) +do + ((COUNT = COUNT + 1)) + TOKEN=$1 + case $TOKEN in + -s|-S) shift 2 + ((COUNT = COUNT + 1)) + ;; + -s${SCALE}) shift + ;; + -S${SCALE}) shift + ;; + *) ((TOTAL_NUMBERS = TOTAL_NUMBERS + 1)) + if ((TOTAL_NUMBERS == 1)) + then + DIVIDEND=$TOKEN + elif ((TOTAL_NUMBERS == 2)) + then + DIVISOR=$TOKEN + else + echo "ERROR: Too many numbers to divide" + usage + exit 1 + fi + NUM_LIST="$NUM_LIST $TOKEN" + ((COUNT < MAX_COUNT)) && shift + ;; + esac +done + +######################################################## + +# Ensure that the scale is an integer value + +case $SCALE in ++([0-9])) : # No-op - Do Nothing + ;; +*) echo "\nERROR: Invalid scale - $SCALE - Must be an integer" + usage + exit 1 + ;; +esac + +######################################################## + +# Check each number supplied to ensure that the "numbers" +# are either integers or floating-point numbers. + +for NUM in $NUM_LIST +do + case $NUM in + +([0-9])) # Check for an integer + : # No-op, do nothing. + ;; + +([-0-9])) # Check for a negative whole number + : # No-op, do nothing + ;; + +([0-9]|[.][0-9])) + # Check for a positive floating-point number + : # No-op, do nothing + ;; + +(+[0-9]|[.][0-9])) + # Check for a positive floating-point number + # with a + prefix + : # No-op, do nothing + ;; + +([-0-9]|.[0-9])) + # Check for a negative floating-point number + : # No-op, do nothing + ;; + +(-.[0-9])) + # Check for a negative floating-point number + : # No-op, do nothing + ;; + +([+.0-9])) + # Check for a positive floating-point number + : # No-op, do nothing + ;; + *) echo "\nERROR: $NUM is NOT a valid number" + usage + exit 1 + ;; + esac +done + +######################################################## + +# Do the math here by using a here document to supply +# input to the bc command. The quotient of the division is +# assigned to the QUOTIENT variable. + +QUOTIENT=$(bc < This script/function exited normally +# 1 ==> Usage or syntax error +# 2 ==> This script/function exited on a trapped signal +# +# REV. LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to debug without any command execution +# +######################################################## +############## DEFINE VARIABLE HERE #################### +######################################################## + +SCRIPT_NAME=$(basename $0) # The name of this shell script +SCALE="0" # Initialize the scale value to zero +NUM_LIST= # Initialize the NUM_LIST to NULL +COUNT=0 # Initialize the counter to zero +MAX_COUNT=$# # Set MAX_COUNT to the total number of + # command-line arguments + +######################################################## +################ FUNCTIONS ############################# +######################################################## + +function usage +{ +echo "\nPURPOSE: Multiplies a list of numbers together\n" +echo "USAGE: $SCRIPT_NAME [-s scale_value] N1 N2...Nn" +echo "\nFor an integer result without any significant decimal places..." +echo "\nEXAMPLE: $SCRIPT_NAME 2048.221 65536 \n" +echo "OR for 4 significant decimal places" +echo "\nEXAMPLE: $SCRIPT_NAME -s 4 8.09838 2048 65536 42.632" +echo "\n\t...EXITING...\n" +} + +######################################################## + +function exit_trap +{ +echo "\n...EXITING on trapped signal...\n" +} + +######################################################## +################# START OF MAIN ######################## +######################################################## + +###### Set a Trap ###### + +trap 'exit_trap; exit 2' 1 2 3 15 + +######################################################## + +# Check for at least two command-line arguments + +if (($# < 2)) +then + echo "\nERROR: Please provide a list of numbers to multiply" + usage + exit 1 +fi + +######################################################## + +# Parse the command-line arguments to find the scale value, if present. + +while getopts ":s:S:" ARGUMENT +do + case $ARGUMENT in + s|S) SCALE=$OPTARG + ;; + \?) # Because we may have negative numbers we need + # to test to see if the ARGUMENT that begins with a + # hyphen (-) is a number, and not an invalid switch!!! + + for TST_ARG in $* + do + if [[ $(echo $TST_ARG | cut -c1) = '-' ]] \ + && [ $TST_ARG != '-s' -a $TST_ARG != '-S' ] + then + case $TST_ARG in + +([-0-9])) : # No-op, do nothing + ;; + +([-0-9].[0-9])) + : # No-op, do nothing + ;; + +([-.0-9])) : # No-op, do nothing + ;; + *) echo "\nERROR: $TST_ARG is an invalid argument\n" + usage + exit 1 + ;; + esac + fi + done + ;; + esac +done + +######################################################## + +# Parse through the command-line arguments and gather a list +# of numbers to multiply together. + +while ((COUNT < MAX_COUNT)) +do + ((COUNT = COUNT + 1)) + TOKEN=$1 + case $TOKEN in + -s|-S) shift 2 + ((COUNT = COUNT + 1)) + ;; + -s${SCALE}) shift + ;; + -S${SCALE}) shift + ;; + *) NUM_LIST="${NUM_LIST} $TOKEN" + ((COUNT < MAX_COUNT)) && shift + ;; + esac +done + +######################################################## + +# Ensure that the scale is an integer value + +case $SCALE in ++([0-9])) : # No-Op - Do Nothing + ;; + *) echo "\nERROR: Invalid scale - $SCALE - Must be an integer" + usage + exit 1 + ;; +esac + +######################################################## + +# Check each number supplied to ensure that the "numbers" +# are either integers or floating-point numbers. + +for NUM in $NUM_LIST +do + case $NUM in + +([0-9])) # Check for an integer + : # No-op, do nothing. + ;; + +([-0-9])) # Check for a negative whole number + : # No-op, do nothing + ;; + +([0-9]|[.][0-9])) + # Check for a positive floating-point number + : # No-op, do nothing + ;; + +(+[0-9]|[.][0-9])) + # Check for a positive floating-point number + # with a + prefix + : # No-op, do nothing + ;; + +([-0-9]|.[0-9])) + # Check for a negative floating-point number + : # No-op, do nothing + ;; + +(-.[0-9])) + # Check for a negative floating-point number + : # No-op, do nothing + ;; + +([+.0-9])) + # Check for a positive floating-point number + : # No-op, do nothing + ;; + *) echo "\nERROR: $NUM is NOT a valid number" + usage + exit 1 + ;; + esac +done + +######################################################## + +# Build the list of numbers to multiply + +MULTIPLY= # Initialize the MULTIPLY variable to NULL +TIMES= # Initialize the TIMES variable to NULL + +# Loop through each number and build a math statement that +# will multiply all of the numbers together. + +for X in $NUM_LIST +do + # If the number has a + prefix, remove it! + if [[ $(echo $X | cut -c1) = '+' ]] + then + X=$(echo $X | cut -c2-) + fi + MULTIPLY="$MULTIPLY $TIMES $X" + TIMES='*' +done + +######################################################## + +# Do the math here by using a here document to supply +# input to the bc command. The product of the multiplication +# of the numbers is assigned to the PRODUCT variable. + +echo "\nSCALE is $SCALE\n" + + +PRODUCT=$(bc < This script completed without error +# 1 ==> Usage error +# 2 ==> This script exited on a trapped signal +# +# REV. LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to debug without any command execution +# +######################################################## +############## DEFINE VARIABLE HERE #################### +######################################################## + +SCRIPT_NAME=`basename $0` # The name of this shell script +SCALE="0" # Initialize the scale value to zero +NUM_LIST= # Initialize the NUM_LIST to NULL +COUNT=0 # Initialize the counter to zero +MAX_COUNT=$# # Set MAX_COUNT to the total number of + # command-line arguments + +######################################################## +################ FUNCTIONS ############################# +######################################################## + +function usage +{ +echo "\nPURPOSE: Subtracts a list of numbers\n" +echo "USAGE: $SCRIPT_NAME [-s scale_value] N1 N2...Nn" +echo "\nFor an integer result without any significant decimal places..." +echo "\nEXAMPLE: $SCRIPT_NAME 2048.221 65536 \n" +echo "OR for 4 significant decimal places" +echo "\nEXAMPLE: $SCRIPT_NAME -s 4 8.09838 2048 65536 42.632" +echo "\n\t...EXITING...\n" +} + +######################################################## + +function exit_trap +{ +echo "\n...EXITING on trapped signal...\n" +} +######################################################## +################ START OF MAIN ######################### +######################################################## + +###### Set a Trap ###### + +trap 'exit_trap; exit 2' 1 2 3 15 + +######################## + +# Check for at least two command-line arguments + +if (($# < 2)) +then + echo "\nERROR: Please provide a list of numbers to subtract" + usage + exit 1 +fi +# Parse the command-line arguments to find the scale value, if present. + +while getopts ":s:S:" ARGUMENT +do + case $ARGUMENT in + s|S) SCALE=$OPTARG + ;; + \?) # Because we may have negative numbers we need + # to test to see if the ARGUMENT that begins with a + # hyphen (-) is a number, and not an invalid switch!!! + + for TST_ARG in $* + do + if [[ $(echo $TST_ARG | cut -c1) = '-' ]] \ + && [ $TST_ARG != '-s' -a $TST_ARG != '-S' ] + then + case $TST_ARG in + +([-0-9])) : # No-op, do nothing + ;; + +([-0-9].[0-9])) + : # No-op, do nothing + ;; + +([-.0-9])) : # No-op, do nothing + ;; + *) echo "\nERROR: Invalid argument on the command line" + usage + exit 1 + ;; + esac + fi + done + ;; + esac +done + +######################################################## + +# Parse through the command-line arguments and gather a list +# of numbers to subtract. + +while ((COUNT < MAX_COUNT)) +do + ((COUNT = COUNT + 1)) + TOKEN=$1 + case $TOKEN in + -s|-S) shift 2 + ((COUNT = COUNT + 1)) + ;; + -s${SCALE}) shift + ;; + -S${SCALE}) shift + ;; + *) NUM_LIST="${NUM_LIST} $TOKEN" + ((COUNT < MAX_COUNT)) && shift + ;; + esac +done + +######################################################## + +# Ensure that the scale is an integer value + +case $SCALE in + +([0-9])) : # No-Op - Do Nothing + ;; + *) echo "\nERROR: Invalid scale - $SCALE - Must be an integer" + usage + exit 1 + ;; +esac + +######################################################## + +# Check each number supplied to ensure that the "numbers" +# are either integers or floating-point numbers. + +for NUM in $NUM_LIST +do + case $NUM in + +([0-9])) # Check for an integer + : # No-op, do nothing. + ;; + +([-0-9])) # Check for a negative whole number + : # No-op, do nothing + ;; + +([0-9]|[.][0-9])) + # Check for a positive floating-point number + : # No-op, do nothing + ;; + +(+[0-9]|[.][0-9])) + # Check for a positive floating-point number + # with a + prefix + : # No-op, do nothing + ;; + +([-0-9]|.[0-9])) + # Check for a negative floating-point number + : # No-op, do nothing + ;; + +(-[.][0-9])) + # Check for a negative floating-point number + : # No-op, do nothing + ;; + +([+.0-9])) + # Check for a positive floating-point number + : # No-op, do nothing + ;; + *) echo "\nERROR: $NUM is NOT a valid number" + usage + exit 1 + ;; + esac +done + +######################################################## + +# Build the list of numbers to subtract + +SUBTRACT= # Initialize the SUBTRACT variable to NULL +MINUS= # Initialize the MINUS variable to NULL + +# Loop through each number and build a math statement that +# will subtract the numbers in the list. + +for X in $NUM_LIST +do + # If the number has a + prefix, remove it! + if [[ $(echo $X | cut -c1) = '+' ]] + then + X=$(echo $X | cut -c2-) + fi + SUBTRACT="$SUBTRACT $MINUS $X" + MINUS='-' +done + +######################################################## + +# Do the math here by using a here document to supply +# input to the bc command. The difference of the numbers is +# assigned to the DIFFERENCE variable. + +DIFFERENCE=$(bc < 36 )) +then + echo "\nERROR: Input base must be between 2 and 36\n" + exit 1 +fi + +# Ask the user for the output base + +echo "\nWhat base do you want to convert $ibase_num to? +NOTE: base 2 through 36 are valid\n" +echo "Output base: \c" +read obase + +# Test to ensure the output base is an integer + +case $obase in + [0-9]*) : # do nothing + ;; + *) echo "\nERROR: $obase is not a valid number\n" + exit 1 + ;; +esac + +# Test to ensure the output base is between 2 and 36 + +if (( obase < 2 || obase > 36 )) +then + echo "\nERROR: Output base must be between 2 and 36\n" + exit 1 +fi + +# Save the input number before changing the base + +in_base_num=$ibase_num + +# Convert the input number to the desire output base + +typeset -i$obase ibase_num + +# Assign the output base number to an appropriate variable name + +obase_num=$ibase_num + +# Display the result + +echo "\nInput number $in_base_num is equivalent to $obase_num\n" + diff --git a/chapter14/chg_base_bc.bash b/chapter14/chg_base_bc.bash new file mode 100755 index 0000000..b69cbc9 --- /dev/null +++ b/chapter14/chg_base_bc.bash @@ -0,0 +1,111 @@ +#!/bin/bash +# +# SCRIPT: chg_base_bc.bash +# AUTHOR: Randy Michael +# DATE: 10/4/2007 +# REV: 1.1.A +# +# PURPOSE: This script converts numbers between base +# 2 through base 16 using the bc utility. The user +# is prompted for input. +# +# NOTE: Numbers are in the following format: +# +# base#number +# +# EXAMPLE: 16#264bf +# +########################################## +# DEFINE FILES AND VARIABLES HERE +########################################## + +# Setup the correct awk usage. Solaris needs to +# use nawk instead of awk. + +case $(uname) in +SunOS) AWK="nawk" + ;; + *) AWK="awk" + ;; +esac + +########################################## +# BEGINNING OF MAIN +########################################## + +# Prompt the user for an input number with base + +echo -e "\nInput the number to convert in the following format: + +Format: base#number + +Example: 16#264BF \n" + +read IBASE_NUM + +# Extract the base from the ibase_num variable + +IBASE=$(echo $IBASE_NUM | $AWK -F '#' '{print $1}') +INUMBER=$(echo $IBASE_NUM | $AWK -F '#' '{print $2}') + +# Test to ensure the input base is between 2 and 16 + +if (( IBASE < 2 || IBASE > 16 )) +then + echo -e "\nERROR: Input base must be between 2 and 16\n" + exit 1 +fi + +# The bc utility requires all number bases greater +# than 10 use uppercase characters for all +# non-numeric character numbers, i.e. hex numbers. +# We use the tr command to upcase all lowercase +# characters. + +if (( IBASE > 10 )) +then + INUMBER=$(echo $INUMBER | tr '[a-z]' '[A-Z]') +fi + +# Ask the user for the output base + +echo -e "\nWhat base do you want to convert $IBASE_NUM to? +NOTE: base 2 through 16 are valid\n" +echo -e "Output base: \c" +read OBASE + +# Test to ensure the output base is an integer + +case $OBASE in + [0-9]*) : # do nothing + ;; + *) echo -e "\nERROR: $obase is not a valid number\n" + exit 1 + ;; +esac + +# Test to ensure the output base is between 2 and 16 + +if (( OBASE < 2 || OBASE > 16 )) +then + echo -e "\nERROR: Output base must be between 2 and 16\n" + exit 1 +fi + +# Save the input number before changing the base + +IN_BASE_NUM=$IBASE_NUM + +# Convert the input number to decimal + +DEC_EQUIV=$(echo "ibase=$IBASE; $INUMBER" | bc) + +# Convert the number to the desired output base + +RESULT=$(echo "obase=$OBASE; $DEC_EQUIV" | bc) + +# Display the result + +echo -e "\nInput number $IN_BASE_NUM is equivalent to \ +${OBASE}#${RESULT}\n" + diff --git a/chapter14/equate_any_base.ksh b/chapter14/equate_any_base.ksh new file mode 100755 index 0000000..d22f6cb --- /dev/null +++ b/chapter14/equate_any_base.ksh @@ -0,0 +1,177 @@ +#!/usr/bin/ksh +# +# SCRIPT: equate_any_base.ksh +# AUTHOR: Randy Michael +# DATE: 07/07/2007 +# REV: 1.2.P +# +# PURPOSE: This script is used to convert a number to any +# supported number base, which is at least base 36. +# This script requires that two command line +# auguments and the "number" to be converted +# are present on the command line. An example +# number base conversion is shown here: +# +# equate_any_base.ksh -f16 -t2 e245c +# 2#11100010010001011100 +# +# This example converts the base 16 number, e245c, to +# the base 2 equivalent, 2#11100010010001011100. +# The 2#, which precedes the binary number, shows +# the base of the number represented. +# +# EXIT CODES: +# 0 - Normal script execution +# 1 - Usage error +# +# set -x # Uncomment to debug this shell script +# set -n # Uncomment to check syntax without any execution +# +###################################################### +# DEFINE FILES AND VARIABLES HERE +###################################################### + +SCRIPT_NAME=$(basename $0) +COUNT=0 +MAX_COUNT=$# + +# Setup the correct echo command usage. Many Linux +# distributions will execute in BASH even if the +# script specifies Korn shell. BASH shell requires +# we use echo -e when we use \n, \c, etc. + +case $SHELL in +*/bin/bash) alias echo="echo -e" + ;; +esac + +###################################################### +# DEFINE FUNCTIONS HERE +###################################################### + +function usage +{ +echo "\nUSAGE: $SCRIPT_NAME -f{starting base} -t{ending base} NUMBER" +echo "\nEXAMPLE: $SCRIPT_NAME -f16 -t10 FC23" +echo "\nWill return the decimal base 10 number 64547" +echo "\t ...EXITING...\n" +} + +###################################################### +# CHECK COMMAND LINE AUGUMENTS HERE +###################################################### + +# The maximum number of command line arguments is five +# and the minimum number is three. + +if (($# > 5)) +then + echo "\nERROR: Too many command line arguments\n" + usage + exit 1 +elif (($# < 3)) +then + echo "\nERROR: Too few command-line arguments\n" + usage + exit 1 +fi + +# Check to see if the command-line switches are present + +echo $* | grep -q '\-f' || (usage; exit 1) +echo $* | grep -q '\-t' || (usage; exit 1) + +# Use getopts to parse the command line arguments + +while getopts ":f:t:" ARGUMENT +do + case $ARGUMENT in + f) START_BASE="$OPTARG" + ;; + t) END_BASE="$OPTARG" + ;; + \?) usage + exit 1 + ;; + esac +done + +# Ensure that the START_BASE and END_BASE variables +# are not NULL. + +if [ -z "$START_BASE" ] || [ "$START_BASE" = '' ] \ + || [ -z "$END_BASE" ] || [ "$END_BASE" = '' ] +then + echo "\nERROR: Base number conversion fields are empty\n" + usage + exit 1 +fi + +# Ensure that the START_BASE and END_BASE variables +# have integer values for the number base conversion. + +case $START_BASE in ++([0-9])) : # Do nothing - Colon is a no-op. + ;; + *) echo "\nERROR: $START_BASE is not an integer value" + usage + exit 1 + ;; +esac + +case $END_BASE in ++([0-9])) : # Do nothing - Colon is a no-op. + ;; + *) echo "\nERROR: $END_BASE is not an integer value" + usage + exit 1 + ;; +esac + +###################################################### +# BEGINNING OF MAIN +###################################################### + +# Begin by finding the BASE_NUM to be converted. + +# Count from 1 to the max number of command line auguments + +while ((COUNT < MAX_COUNT)) +do + ((COUNT == COUNT + 1)) + TOKEN=$1 + case $TOKEN in + -f) shift; shift + ((COUNT == COUNT + 1)) + ;; + -f${START_BASE}) shift + ;; + -t) shift; shift + ((COUNT == COUNT + 1)) + ;; + -t${END_BASE}) shift + ;; + *) BASE_NUM=$TOKEN + break + ;; + esac +done + +# Typeset the RESULT variable to the target number base + +typeset -i$END_BASE RESULT + +# Assign the BASE_NUM variable to the RESULT variable +# and add the starting number base with a pound sign (#) +# as a prefix for the conversion to take place. +# NOTE: If an invalid number is entered a system error +# will be displayed. An example is inputting 1114400 as +# a binary number, which is invalid for a binary number. + +RESULT="${START_BASE}#${BASE_NUM}" + +# Display the result to the user or calling program. + +echo "$RESULT" + +# End of script... diff --git a/chapter14/equate_base_10_to_16.ksh b/chapter14/equate_base_10_to_16.ksh new file mode 100755 index 0000000..16c742e --- /dev/null +++ b/chapter14/equate_base_10_to_16.ksh @@ -0,0 +1,115 @@ +#!/usr/bin/ksh +# +# SCRIPT: equate_base_10_to_16.ksh +# AUTHOR: Randy Michael +# DATE: 07/07/2007 +# REV: 1.2.P +# +# PURPOSE: This script is used to convert a base 10 number +# to a base 16 hexadecimal representation. +# This script expects that a base 10 number +# is supplied as a single argument. +# +# EXIT CODES: +# 0 - Normal script execution +# 1 - Usage error +# +# +# REV LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check command syntax without any execution +# +# +################################################# +# DEFINE FILES AND VARIABLES HERE +################################################# + +SCRIPT_NAME=`basename $0` + +# Set up the correct echo command usage. Many Linux +# distributions will execute in Bash even if the +# script specifies Korn shell. Bash shell requires +# we use echo -e when we use \n, \c, etc. + +case $(basename $SHELL) in +bash) alias echo="echo -e" + ;; +esac + +################################################# +# DEFINE FUNCTIONS HERE +################################################# +function usage +{ +echo "\nUSAGE: $SCRIPT_NAME {base 10 number}" +echo "\nEXAMPLE: $SCRIPT_NAME 694" +echo "\nWill return the hexadecimal number 2b6" +echo "\n\t...EXITING...\n" +} + +################################################# +# BEGINNING OF MAIN +################################################# + +# Check for a single command-line argument + +if [ $# -ne 1 ] +then + echo "\nERROR: A base 10 number must be supplied..." + usage + exit 1 +fi + +# Check that this single command-line argument is a base 10 number! +case $1 in ++([0-9])) BASE_10_NUM=$1 + ;; +*) echo "\nERROR: $1 is NOT a base 10 number" + usage + exit 1 + ;; +esac + +# Assign the base 10 number to the BASE_16_NUM variable + +BASE_16_NUM=10#$BASE_10_NUM + +# NOTE: Since base 10 is implied by default, +# the 10# may be omitted as a prefix. Both notations +# are supported + +# Now typeset the BASE_16_NUM variable to base 16. +# This step converts the base 10 number to a base 16 number. + +typeset -i16 BASE_16_NUM + +# Display the resulting base 16 number representation + +echo $BASE_16_NUM + +# This following code is optional. It removes the number base +# prefix. This may be helpful if using this script with +# other programs and scripts. +# +# Set up the correct awk usage. Solaris needs to +# use nawk instead of awk. +# +# case $(uname) in +# SunOS) AWK="nawk" +# ;; +# *) AWK="awk" +# ;; +# esac +# +# Strip out the base prefix and the pound sign (#). (Optional) +# +# echo $BASE_16_NUM | grep -q "#" +# +# if (($? == 0)) +# then +# echo $BASE_16_NUM | $AWK -F '#' '{print $2}' +# else +# echo $BASE_16_NUM +# fi diff --git a/chapter14/equate_base_10_to_2.ksh b/chapter14/equate_base_10_to_2.ksh new file mode 100755 index 0000000..52b69fb --- /dev/null +++ b/chapter14/equate_base_10_to_2.ksh @@ -0,0 +1,75 @@ +#!/usr/bin/ksh +# +# SCRIPT: equate_base_10_to_2.ksh +# AUTHOR: Randy Michael +# DATE: 07/07/2007 +# REV: 1.2.P +# +# PURPOSE: This script is used to convert a base 10 number +# to a base 2 binary representation. +# This scripts expects that a base 10 number +# is supplied as a single argument. +# +# EXIT CODES: +# 0 - Normal script execution +# 1 - Usage error +# +# REV LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check command syntax without any execution +# +################################################# +# DEFINE FILES AND VARIABLES HERE +################################################# + +SCRIPT_NAME=`basename $0` +typeset -i2 BASE_2_NUM +typeset -i BASE_10_NUM + +# Set up the correct echo command usage. Many Linux +# distributions will execute in Bash even if the +# script specifies Korn shell. Bash shell requires +# we use echo -e when we use \n, \c, etc. + +case $SHELL in +*/bin/bash) alias echo="echo -e" + ;; +esac + +################################################# +# DEFINE FUNCTIONS HERE +################################################# + +function usage +{ +echo "\nUSAGE: $SCRIPT_NAME base_10_number" +echo "\nEXAMPLE: $SCRIPT_NAME 14" +echo "\nWill return the decimal base 2 number 1110 ...EXITING...\n" +} + +################################################# +# BEGINNING OF MAIN +################################################# + +# Check for a single command line argument + +if [ $# -ne 1 ] +then + echo "\nERROR: A base 10 number must be supplied..." + usage + exit 1 +fi + +BASE_10_NUM="$1" + +BASE_2_NUM=$((10#${BASE_10_NUM})) + +echo $BASE_2_NUM | grep "#" >/dev/null 2>&1 +if [ $? -eq 0 ] +then + echo $BASE_2_NUM | cut -f2 -d "#" +else + echo $BASE_2_NUM +fi diff --git a/chapter14/equate_base_10_to_8.ksh b/chapter14/equate_base_10_to_8.ksh new file mode 100755 index 0000000..ba250ab --- /dev/null +++ b/chapter14/equate_base_10_to_8.ksh @@ -0,0 +1,74 @@ +#!/usr/bin/ksh +# +# SCRIPT: equate_base_10_to_8.ksh +# AUTHOR: Randy Michael +# DATE: 07/07/2007 +# REV: 1.2.P +# +# PURPOSE: This script is used to convert a base 10 number +# to a base 8 octal representation. +# This scripts expects that a base 10 number +# is supplied as a single argument. +# +# EXIT CODES: +# 0 - Normal script execution +# 1 - Usage error +# +# REV LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check command syntax without any execution +# +################################################# +# DEFINE FILES AND VARIABLES HERE +################################################# + +SCRIPT_NAME=`basename $0` +typeset -i8 BASE_8_NUM +typeset -i BASE_10_NUM + +# Set up the correct echo command usage. Many Linux +# distributions will execute in Bash even if the +# script specifies Korn shell. Bash shell requires +# we use echo -e when we use \n, \c, etc. + +case $SHELL in +*/bin/bash) alias echo="echo -e" + ;; +esac + +################################################# +# DEFINE FUNCTIONS HERE +################################################# + +function usage +{ +echo "\nUSAGE: $SCRIPT_NAME base_10_number" +echo "\nEXAMPLE: $SCRIPT_NAME 946" +echo "\nWill return the octal base 8 number 1662 ...EXITING...\n" +} + +################################################# +# BEGINNING OF MAIN +################################################# + +# Check for a single command line argument + +if [ $# -ne 1 ] +then + echo "\nERROR: A base 10 number must be supplied..." + usage + exit 1 +fi + +BASE_10_NUM="$1" + +BASE_8_NUM=$((10#${BASE_10_NUM})) +echo $BASE_8_NUM | grep "#" >/dev/null 2>&1 +if [ $? -eq 0 ] +then + echo $BASE_8_NUM | cut -f2 -d "#" +else + echo $BASE_8_NUM +fi diff --git a/chapter14/equate_base_16_to_10.ksh b/chapter14/equate_base_16_to_10.ksh new file mode 100755 index 0000000..21dc670 --- /dev/null +++ b/chapter14/equate_base_16_to_10.ksh @@ -0,0 +1,66 @@ +#!/usr/bin/ksh +# +# SCRIPT: equate_base_16_to_10.ksh +# AUTHOR: Randy Michael +# DATE: 07/07/2007 +# REV: 1.2.P +# +# PURPOSE: This script is used to convert a base 16 number +# to a base 10 decimal representation. +# This scripts expects that a base 16 number +# is supplied as a single argument. +# +# EXIT CODES: +# 0 - Normal script execution +# 1 - Usage error +# +# REV LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check command syntax without any execution +# +################################################# +# DEFINE FILES AND VARIABLES HERE +################################################# + +SCRIPT_NAME=`basename $0` + +# Set up the correct echo command usage. Many Linux +# distributions will execute in Bash even if the +# script specifies Korn shell. Bash shell requires +# we use echo -e when we use \n, \c, etc. + +case $SHELL in +*/bin/bash) alias echo="echo -e" + ;; +esac + +################################################# +# DEFINE FUNCTIONS HERE +################################################# + +function usage +{ +echo "\nUSAGE: $SCRIPT_NAME base_16_number" +echo "\nEXAMPLE: $SCRIPT_NAME FC23" +echo "\nWill return the decimal base 10 number 64547 ...EXITING...\n" +} + +################################################# +# BEGINNING OF MAIN +################################################# + +# Check for a single command line argument + +if [ $# -ne 1 ] +then + echo "\nERROR: A base 16 number must be supplied..." + usage + exit 1 +fi + +BASE_16_NUM="$1" + +BASE_10_NUM=$((16#${BASE_16_NUM})) +echo $BASE_10_NUM diff --git a/chapter14/equate_base_16_to_2.ksh b/chapter14/equate_base_16_to_2.ksh new file mode 100755 index 0000000..60e93a7 --- /dev/null +++ b/chapter14/equate_base_16_to_2.ksh @@ -0,0 +1,74 @@ +#!/usr/bin/ksh +# +# SCRIPT: equate_base_16_to_2.ksh +# AUTHOR: Randy Michael +# DATE: 07/07/2007 +# REV: 1.2.P +# +# PURPOSE: This script is used to convert a base 16 hexidecimal +# number to a base 2 binary representation. +# This scripts expects that a base 16 number +# is supplied as a single argument. +# +# EXIT CODES: +# 0 - Normal script execution +# 1 - Usage error +# +# REV LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check command syntax without any execution +# +################################################# +# DEFINE FILES AND VARIABLES HERE +################################################# + +SCRIPT_NAME=`basename $0` + +# Set up the correct echo command usage. Many Linux +# distributions will execute in Bash even if the +# script specifies Korn shell. Bash shell requires +# we use echo -e when we use \n, \c, etc. + +case $SHELL in +*/bin/bash) alias echo="echo -e" + ;; +esac + +################################################# +# DEFINE FUNCTIONS HERE +################################################# + +function usage +{ +echo "\nUSAGE: $SCRIPT_NAME base_16_number" +echo "\nEXAMPLE: $SCRIPT_NAME FC23" +echo "\nWill return the binary base 2 number 64547 ...EXITING...\n" +} + +################################################# +# BEGINNING OF MAIN +################################################# + +# Check for a single command line argument + +if [ $# -ne 1 ] +then + echo "\nERROR: A base 16 number must be supplied..." + usage + exit 1 +fi + +# Check for a hexidecimal number! + +case $1 in ++([-0-9]|[a-f]|[A-F])) BASE_16_NUM=$1 + ;; + *) usage + ;; +esac + +BASE_2_NUM=$((16#${BASE_16_NUM})) +typeset -i2 BASE_2_NUM +echo $BASE_2_NUM diff --git a/chapter14/equate_base_2_to_10.ksh b/chapter14/equate_base_2_to_10.ksh new file mode 100755 index 0000000..463aa06 --- /dev/null +++ b/chapter14/equate_base_2_to_10.ksh @@ -0,0 +1,82 @@ +#!/usr/bin/ksh +# +# SCRIPT: equate_base_2_to_10.ksh +# AUTHOR: Randy Michael +# DATE: 07/07/2007 +# REV: 1.2.P +# +# PURPOSE: This script is used to convert a base 2 number +# to a base 10 decimal representation. +# This scripts expects that a base 2 number +# is supplied as a single argument. +# +# EXIT CODES: +# 0 - Normal script execution +# 1 - Usage error +# +# REV LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check command syntax without any execution +# +################################################# +# DEFINE FILES AND VARIABLES HERE +################################################# + +SCRIPT_NAME=`basename $0` +typeset -i BASE_2_NUM +typeset -i BASE_10_NUM + +# Set up the correct echo command usage. Many Linux +# distributions will execute in Bash even if the +# script specifies Korn shell. Bash shell requires +# we use echo -e when we use \n, \c, etc. + +case $SHELL in +*/bin/bash) alias echo="echo -e" + ;; +esac + +################################################# +# DEFINE FUNCTIONS HERE +################################################# + +function usage +{ +echo "\nUSAGE: $SCRIPT_NAME base_2_number" +echo "\nEXAMPLE: $SCRIPT_NAME 1110" +echo "\nWill return the decimal base 10 number 14 ...EXITING...\n" +} + +################################################# +# BEGINNING OF MAIN +################################################# + +# Check for a single command line argument + +if [ $# -ne 1 ] +then + echo "\nERROR: A base 2 number must be supplied..." + usage + exit 1 +fi + +# Check that this single command line argument is a binary number! + +case $1 in ++([-0-1])) BASE_2_NUM=$1 + ;; + *) echo "\nERROR: $1 is NOT a base 2 number" + usage + exit 1 + ;; +esac + +# Assign the base 2 number to the BASE_10_NUM variable + +BASE_2_NUM="$1" + +BASE_10_NUM=$((2#${BASE_2_NUM})) + +echo $BASE_10_NUM diff --git a/chapter14/equate_base_2_to_16.ksh b/chapter14/equate_base_2_to_16.ksh new file mode 100755 index 0000000..9c7c6c2 --- /dev/null +++ b/chapter14/equate_base_2_to_16.ksh @@ -0,0 +1,87 @@ +#!/usr/bin/ksh +# +# SCRIPT: equate_base_2_to_16.ksh +# AUTHOR: Randy Michael +# DATE: 07/07/2007 +# REV: 1.2.P +# +# PURPOSE: This script is used to convert a base 2 number +# to a base 16 hexadecimal representation. +# This scripts expects that a base 2 number +# is supplied as a single argument. +# +# EXIT CODES: +# 0 - Normal script execution +# 1 - Usage error +# +# REV LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check command syntax without any execution +# +################################################# +# DEFINE FILES AND VARIABLES HERE +################################################# + +SCRIPT_NAME=`basename $0` + +# Set up the correct echo command usage. Many Linux +# distributions will execute in Bash even if the +# script specifies Korn shell. Bash shell requires +# we use echo -e when we use \n, \c, etc. + +case $SHELL in +*/bin/bash) alias echo="echo -e" + ;; +esac + +################################################# +# DEFINE FUNCTIONS HERE +################################################# + +function usage +{ +echo "\nUSAGE: $SCRIPT_NAME {base 2 number}" +echo "\nEXAMPLE: $SCRIPT_NAME 1100101101" +echo "\nWill return the hexadecimal base 16 number 32d" +echo "\n\t ...EXITING...\n" +} + +################################################# +# BEGINNING OF MAIN +################################################# + +# Check for a single command line argument + +if (($# != 1)) +then + echo "\nERROR: A base 2 number must be supplied..." + usage + exit 1 +fi + +# Check that this single command line argument is a binary number! + +case $1 in ++([-0-1])) BASE_2_NUM=$1 + ;; + *) echo "\nERROR: $1 is NOT a base 2 number" + usage + exit 1 + ;; +esac + +# Assign the base 2 number to the BASE_16_NUM variable + +BASE_16_NUM=$((2#${BASE_2_NUM})) + +# Now typeset the BASE_16_NUM variable to base 16. +# This step converts the base 2 number to a base 16 number. + +typeset -i16 BASE_16_NUM + +# Display the resulting base 16 representation + +echo $BASE_16_NUM + diff --git a/chapter14/equate_base_8_to_10.ksh b/chapter14/equate_base_8_to_10.ksh new file mode 100755 index 0000000..326fe91 --- /dev/null +++ b/chapter14/equate_base_8_to_10.ksh @@ -0,0 +1,68 @@ +#!/usr/bin/ksh +# +# SCRIPT: equate_base_8_to_10.ksh +# AUTHOR: Randy Michael +# DATE: 07/07/2007 +# REV: 1.2.P +# +# PURPOSE: This script is used to convert a base 8 number +# to a base 10 decimal representation. +# This scripts expects that a base 8 number +# is supplied as a single argument. +# +# EXIT CODES: +# 0 - Normal script execution +# 1 - Usage error +# +# REV LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check command syntax without any execution +# +################################################# +# DEFINE FILES AND VARIABLES HERE +################################################# + +SCRIPT_NAME=`basename $0` +typeset -i BASE_8_NUM +typeset -i BASE_10_NUM + +################################################# +# DEFINE FUNCTIONS HERE +################################################# + +function usage +{ +echo "\nUSAGE: $SCRIPT_NAME base_8_number" +echo "\nEXAMPLE: $SCRIPT_NAME 2774" +echo "\nWill return the decimal base 10 number 1532 ...EXITING...\n" +} + +################################################# +# BEGINNING OF MAIN +################################################# + +# Check for a single command line argument + +if [ $# -ne 1 ] +then + echo "\nERROR: A base 8 number must be supplied..." + usage + exit 1 +fi + +# Check that this single command line argument is a octal number! + +case $1 in ++([-0-8])) BASE_8_NUM=$1 + ;; + *) echo "\nERROR: $1 is NOT a base 7 number" + usage + exit 1 + ;; +esac + +BASE_10_NUM=$((8#${BASE_8_NUM})) + +echo $BASE_10_NUM diff --git a/chapter14/mk_swkey.ksh b/chapter14/mk_swkey.ksh new file mode 100755 index 0000000..4b6347f --- /dev/null +++ b/chapter14/mk_swkey.ksh @@ -0,0 +1,103 @@ +#!/usr/bin/ksh +# +# SCRIPT: mk_swkey.ksh +# AUTHOR: Randy Michael +# DATE: 07/07/2007 +# REV: 1.2.P +# +# PURPOSE: This script is used to create a software +# license key based on the IP address of the +# system that this shell script is executed on. +# The system is queried for the system's IP +# address. The IP address is striped of the +# dots (.) and each number is converted to +# hexadecimal. Then each hex string is combined +# into a single hex string, which is the software +# license key. +# +# REV LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check command syntax without any execution +# +################################################# +# DEFINE FILES AND VARIABLES HERE +################################################# + +# Set up the correct echo command usage. Many Linux +# distributions will execute in Bash even if the +# script specifies Korn shell. Bash shell requires +# we use echo -e when we use \n, \c, etc. + +case $SHELL in +*/bin/bash) alias echo="echo -e" + echo -e "\nWARNING: This script is executing in Bash shell." + echo "This script may fail because of an unsupported typeset" + echo -e "command option that is only supported in Korn shell\n" + ;; +esac + +# Set up the correct awk usage. Solaris needs to +# use nawk instead of awk. + +case $(uname) in +SunOS) alias awk="nawk" + ;; +esac + +################################################# +# DEFINE FUNCTIONS HERE +################################################# + +function convert_base_10_to_16 +{ +# set -x # Uncomment to debug this function + +typeset -i16 BASE_16_NUM + +BASE_10_NUM=$1 + +BASE_16_NUM=$((10#${BASE_10_NUM})) + +# Strip the number base prefix from the hexadecimal +# number. This prefix is not needed here. + +echo $BASE_16_NUM | grep -q '#' +if (($? == 0)) +then + echo $BASE_16_NUM | awk -F '#' '{print $2}' +else + echo $BASE_16_NUM +fi +} + +############################################################# +# BEGINNING OF MAIN +############################################################# + +# Query the system for the IP address using the "host $(hostname)" +# command substitution. + +IP=$(host $(hostname) | awk '{print $3}' | awk -F ',' '{print $1}') + +# Field delimit the IP address on the dots (.) and assign each +# number to a separate variable in a "while read" loop. + +echo $IP | awk -F '.' '{print $1, $2, $3, $4}' | while read a b c d junk +do + # Convert each of the numbers in the IP address + # into hexadecimal by calling the "convert_base_10_to16" + # function. + + FIRST=$(convert_base_10_to_16 $a) + SECOND=$(convert_base_10_to_16 $b) + THIRD=$(convert_base_10_to_16 $c) + FORTH=$(convert_base_10_to_16 $d) +done + +# Combine all of the hexadecimal strings into a single +# hexadecimal string, which represents the software key. + +echo "${FIRST}${SECOND}${THIRD}${FORTH}" + diff --git a/chapter15/hgrep.bash b/chapter15/hgrep.bash new file mode 100755 index 0000000..3b0ee14 --- /dev/null +++ b/chapter15/hgrep.bash @@ -0,0 +1,206 @@ +#!/bin/bash +# +# SCRIPT: hgrep.bash +# AUTHOR: Randy Michael +# DATE: 07/09/2007 +# REV 3.0 +# +# PLATFORM: Not Platform Dependent +# +# PURPOSE: This script is used to highlight text in a file. +# Given a text string and a file the script will search +# the file for the specified string pattern and highlight +# each occurrence. For standard input the script pages a +# temporary file which has the string text highlighted. +# +# REV LIST: +# +# set -x # Uncomment to debug +# set -n # Uncomment to check script syntax without execution +# +# EXIT CODES: +# 0 ==> Script exited normally +# 1 ==> Usage error +# 2 ==> Input File Error +# 3 ==> Pattern not found in the file +# +# REV LIST: +# 03/12/2001 - Randy Michael - Sr. Sys. Admin. +# Added code to just exit if the string is not in +# the target file. +# +# 03/13/2001 - Randy Michael - Sr. Sys. Admin. +# Added code to ensure the target file is a readable +# "regular" non-zero file. +# +# 03/13/2001 - Randy Michael - Sr. Sys. Admin. +# Added code to highlight the text string and filename +# in the error and information messages. +# +# 08-22-2001 - Randy Michael - Sr. Sys. Admin +# Changed the code to allow this script to accept standard +# input from a pipe. This makes the script work more like the +# grep command +# +# 7/02/2007 - Randy Michael - Sr. Sys. Admin +# Converted this script to BASH shell. +# +############################################## +# DEFINE FILES AND VARIABLES HERE +############################################## + +SCRIPT_NAME=$(basename $0) +OUTPUT_FILE="/tmp/highlightfile.out" +>$OUTPUT_FILE + +############################################## +# DEFINE FUNCTIONS HERE +############################################## + +function usage +{ +echo -e "\nUSAGE: $SCRIPT_NAME pattern [filename]\n" +} + +############################################## +# CHECK COMMAND SYNTAX +############################################## + +# Input coming from standard input + +if (( $# == 1 )) +then + # Input coming from standard input + + PATTERN="$1" # Pattern to highlight + FILENAME= # Assign NULL to FILENAME + +elif (( $# == 2 )) +then + # Input coming from $FILENAME file + + PATTERN="$1" # Pattern to highlight + FILENAME="$2" # File to use as input + + # Does the file exist as a "regular" file? + + if [ ! -f "$FILENAME" ] + then + echo -e "\nERROR: $FILENAME does not exist...\n" + usage + exit 2 + fi + + # Is the file empty? + + if [ ! -s "$FILENAME" ] + then + echo -e "\nERROR: \c" + tput smso + echo -e "$FILENAME\c" + tput sgr0 + echo -e " file size is zero...nothing to search\n" + usage + exit 2 + fi + + # Is the file readable by this script? + + if [ ! -r "$FILENAME" ] + then + echo -e "\nERROR: \c" + tput smso + echo -e "${FILENAME}\c" + tput sgr0 + echo -e " is not readable to this program...\n" + usage + exit 2 + fi + + # Is the pattern anywhere in the file? + + grep -q "$PATTERN" "$FILENAME" + if (( $? != 0 )) + then + echo -e "\nSORRY: The string \c" + tput smso + echo -e "${PATTERN}\c" + tput sgr0 + echo -e " was not found in \c" + tput smso + echo -e "${FILENAME}\c" + tput sgr0 + echo -e "\n\n....EXITING...\n" + exit 3 + fi +else + # Incorrect number of command-line arguments + usage + exit 1 +fi + +############################################## +# BEGINNING OF MAIN +############################################## + +# There is no $FILENAME if we get input from a pipe... + +if [[ ! -z "$FILENAME" && "$FILENAME" != '' ]] +then + # Using $FILENAME as input + + case $(uname) in + AIX|HP-UX) + # This is a fancy "pg" command. It acts similar to the + # "more" command but instead of showing the percentage + # displayed it shows the page number of the file + + sed s/"${PATTERN}"/$(tput smso)"${PATTERN}"$(tput sgr0)/g \ + "$FILENAME" | pg -csn -p"Page %d:" + exit 0 + ;; + *) + sed s/"${PATTERN}"/$(tput smso)"${PATTERN}"$(tput sgr0)/g \ + "$FILENAME" | more + exit 0 + ;; + esac +else + # Input is from standard input... + sed s/"${PATTERN}"/$(tput smso)"${PATTERN}"$(tput sgr0)/g \ + > $OUTPUT_FILE + + # Is the pattern anywhere in the file? + grep -q "$PATTERN" $OUTPUT_FILE + if (( $? != 0 )) + then + echo -e "\nERROR: The string \c" + tput smso + echo -e "${PATTERN}\c" + tput sgr0 + echo -e " was not found in standard input\c" + echo -e "\n\n....EXITING...\n" + exit 3 + fi +fi + +# Check the operating system, on AIX and HP-UX we need to +# use the "pg", or "page" command. The "more" command does +# not work to highlight the text, it will only show the +# characters that make up the escape sequence. All +# other operating system usr the "more" command. + +case $(uname) in +AIX|HP-UX) + + # This is a fancy "pg" command. It acts similar to the + # "more" command but instead of showing the percentage + # displayed it shows the page number of the file + + cat $OUTPUT_FILE | pg -csn -p"Page %d:" + ;; +*) + cat $OUTPUT_FILE | more + ;; +esac + diff --git a/chapter15/hgrep.ksh b/chapter15/hgrep.ksh new file mode 100755 index 0000000..df295c7 --- /dev/null +++ b/chapter15/hgrep.ksh @@ -0,0 +1,195 @@ +#!/usr/bin/ksh +# +# SCRIPT: hgrep.ksh +# AUTHOR: Randy Michael +# DATE: 03/09/2001 +# REV 2.1.P +# +# PLATFORM: Not Platform Dependent +# +# PURPOSE: This script is used to highlight text in a file. Given a text +# string and a file the script will search the file for the specified +# string and highlight each occurrence of the string by paging +# a temporary file which has the string text highlighted. +# +# REV LIST: +# +# set -x # Uncomment to debug +# set -n # Uncomment to check command syntax without execution +# +# EXIT CODES: +# +# 0 ==> Script exited normally +# 1 ==> Usage error +# 2 ==> Input File Error +# 3 ==> Pattern not found in the file +# +# REV LIST: +# 03/12/2001 - Randy Michael - Sr. Sys. Admin. +# Added code to just exit if the string is not in +# the target file. +# +# 03/13/2001 - Randy Michael - Sr. Sys. Admin. +# Added code to ensure the target file is a readable "regular" +# non-zero file. +# +# 03/13/2001 - Randy Michael - Sr. Sys. Admin. +# Added code to highlight the text string and filename +# in the error and information messages. +# +# 08-22-2001 - Randy Michael - Sr. Sys. Admin +# Changed the code to allow this script to accept standard +# input from a pipe. This makes the script work more like the +# grep command + +SCRIPT_NAME=`basename $0` + +############################################## +########### DEFINE FUNCTIONS HERE ############ +############################################## + +function usage +{ + echo "\nUSAGE: $SCRIPT_NAME pattern [filename]\n" +} + +############################################## +########### CHECK COMMAND SYNTAX ############# +############################################## + + +# Input coming from standard input + +if [ $# -eq 1 ] +then + # Input coming from standard input + + PATTERN="$1" # Pattern to highlight + FILENAME= # Assign NULL to FILENAME + +elif [ $# -eq 2 ] +then + # Input coming from $FILENAME file + + PATTERN="$1" # Pattern to highlight + FILENAME="$2" # File to use as input + + # Does the file exist as a "regular" file? + + if [ ! -f $FILENAME ] + then + echo "\nERROR: $FILENAME does not exist...\n" + usage + exit 2 + fi + + # Is the file empty? + + if [ ! -s $FILENAME ] + then + echo "\nERROR: \c" + tput smso + echo "$FILENAME\c" + tput sgr0 + echo " file size is zero...nothing to search\n" + usage + exit 2 + fi + + # Is the file readable by this script? + + if [ ! -r $FILENAME ] + then + echo "\nERROR: \c" + tput smso + echo "${FILENAME}\c" + tput sgr0 + echo " is not readable to this program...\n" + usage + exit 2 + fi + + # Is the pattern anywhere in the file? + + grep "$PATTERN" $FILENAME >/dev/null 2>&1 + if [ $? -ne 0 ] + then + echo "\nSORRY: The string \c" + tput smso + echo "${PATTERN}\c" + tput sgr0 + echo " was not found in \c" + tput smso + echo "${FILENAME}\c" + tput sgr0 + echo "\n\n....EXITING...\n" + exit 3 + fi +else + # Incorrect number of command line arguments + + usage + exit 1 +fi + +############################################## +########### DEFINE VARIABLES HERE ############ +############################################## + +OUTPUT_FILE="/tmp/highlightfile.out" +>$OUTPUT_FILE + +############################################## +############ START OF MAIN ################### +############################################## + +# There is no $FILENAME if we get input from a pipe... + +if [[ ! -z "$FILENAME" && $FILENAME != '' ]] +then + # Using $FILENAME as input + + cat "$FILENAME" \ + | sed s/"${PATTERN}"/$(tput smso)"${PATTERN}"$(tput sgr0)/g \ + > $OUTPUT_FILE +else + # Input is from standard input... + + sed s/"${PATTERN}"/$(tput smso)"${PATTERN}"$(tput sgr0)/g \ + > $OUTPUT_FILE + + # Is the pattern anywhere in the file? + + grep "$PATTERN" $OUTPUT_FILE >/dev/null 2>&1 + if [ $? -ne 0 ] + then + echo "\nSORRY: The string \c" + tput smso + echo "${PATTERN}\c" + tput sgr0 + echo " was not found in standard input\c" + echo "\n\n....EXITING...\n" + exit 3 + fi + +fi + +# Check the operating system, on AIX and HP-UX we need to +# use the "pg", or "page" command. The "more" command does +# not work to highlight the text, it will only show the +# characters that make up the escape sequence. All +# other operating system usr the "more" command. + +case $(uname) in +AIX|HP-UX) + + # This is a fancy "pg" command. It acts similar to the + # "more" command but instead of showing the percentage + # displayed it shows the page number of the file + + cat $OUTPUT_FILE | pg -csn -p"Page %d:" + ;; +*) + cat $OUTPUT_FILE | more + ;; +esac diff --git a/chapter16/function_check_HTTP_server b/chapter16/function_check_HTTP_server new file mode 100755 index 0000000..307f2cb --- /dev/null +++ b/chapter16/function_check_HTTP_server @@ -0,0 +1,35 @@ +check_HTTP_server () +{ +LINX="/usr/local/bin/lynx" # Define the location of the linx program +URL=$1 # Capture the target URL in the $1 position +URLFILE=/tmp/HTTP.$$ # Define a file to hold the URL output + +########################################### + +$LINX "$URL" > $URLFILE # Attempt to reach the target URL + +if (($? != 0)) # If the URL is unreachable - No Connection +then + echo "\n$URL - Unable to connect\n" + cat $URLFILE +else # Else the URL was found + + while read VER RC STATUS # This while loop is fed from the bottom + # after the "done" using input redirection + do + case $RC in # Check the return code in the $URLFILE + + 200|401|301|302) # These are valid return codes! + echo "\nHTTP Server is OK\n" + ;; + *) # Anything else is not a valid return code + + echo "\nERROR: HTTP Server Error\n" + ;; + esac + + done < $URLFILE +fi + +rm -f $URLFILE +} diff --git a/chapter16/keyit b/chapter16/keyit new file mode 100755 index 0000000..ef5baa5 --- /dev/null +++ b/chapter16/keyit @@ -0,0 +1,197 @@ +#!/usr/bin/ksh +# +# SCRIPT: keyit +# AUTHOR: Randy Michael +# DATE: 7/31/2007 +# REV: 1.0 +# PLATFORM: Not platform dependent +# REQUIREMENTS: OpenSSH +# +# PURPOSE: This script is used to set up +# encryption keypairs between two hosts. +# +# set -x # Uncomment to debug this script +# +# set -n # Uncomment to check script syntax +# # without any execution. Do not +# # forget to add the comment back, +# # or the script will never execute. +# +# USAGE: keyit remote_host username +# +####################################### +# DEFINE FILES AND VARIABLES HERE +####################################### + +RHOST=$1 +THIS_USER=$2 +THIS_SCRIPT=$(basename $0) +THIS_HOST=$(hostname) + +####################################### +# DEFINE FUNCTIONS HERE +####################################### + +usage () +{ +echo "\nUSAGE: $THIS_SCRIPT \ +remote_host username\n" +} + +####################################### + +success_message () +{ +KTYPE=$1 +echo "\nSUCCESS: $KTYPE key pairs configured for $THIS_USER on $RHOST" +echo "\n$THIS_USER should no longer require an SSH password on $RHOST" +echo "when logging in directly, however, using the ssh commands:\n" +echo "\tssh -l $THIS_USER $RHOST\nAND\n\tssh ${THIS_USER}@${RHOST}" +echo "\nWHILE LOGGED IN LOCALLY AS ANOTHER USER will still not work" +echo "without a valid user password\n" +} + +####################################### + +failure_message () +{ +echo "\nERROR: Setting up the $KEYTYPE key pairs failed" +echo "Ensure that OpenSSH is installed and running" +echo "on both hosts. Then ensure that the user has" +echo "a .ssh directory in their \$HOME directory." +echo "See the man page on ssh and ssh-keygen" +echo "for more details and manual setup\n" +} + +####################################### + +keyit_dsa () +{ +# Append the local public key to the same user's +# authorized_users file + +cat ~${THIS_USER}/.ssh/id_dsa.pub | ssh ${THIS_USER}@$RHOST \ +"cat >> ~${THIS_USER}/.ssh/authorized_keys" + +if (( $? == 0 )) +then + success_message dsa +else + failure_message +fi +} + +####################################### + +keyit_rsa () +{ +# Append the local public key to the same user's +# authorized_users file + +cat ~${THIS_USER}/.ssh/id_rsa.pub | ssh ${THIS_USER}@$RHOST \ +"cat >> ~${THIS_USER}/.ssh/authorized_keys" + +if (( $? == 0 )) +then + success_message rsa +else + failure_message +fi +} + +####################################### +# BEGINNING OF MAIN +####################################### + +# Ensure the user $THIS_USER exists on the local system + +if ! $(/usr/bin/id $THIS_USER >/dev/null 2>&1) +then + echo "\nERROR: $THIS_USER is not a valid user on $THIS_HOST\n" + usage + exit 1 +fi + +# Ensure ssh is installed locally + +if [[ ! -x /usr/bin/ssh && ! -x /usr/local/bin/ssh ]] +then + echo "\nERROR: SSH does not appear to be installed on this machine" + echo "This script requires SSH...Exiting...\n" + usage + exit 2 +fi + +# Check for proper usage + +if ! [ $2 ] +then + usage + exit 1 +fi + +# Ping the remote host 1 ping + +if ! $(ping -c1 $RHOST >/dev/null 2>&1) +then + echo "\nERROR: $RHOST is not pingable...Exiting...\n" + exit 2 +fi + +# Set up the key pairs for the configured key(s) + +SET=0 + +if [ -s ~$THIS_USER/.ssh/id_dsa.pub ] +then + keyit_dsa + SET=1 +fi + +if [ -s ~$THIS_USER/.ssh/id_rsa.pub ] +then + keyit_rsa + SET=2 +fi + +if (( SET == 0 )) +then + echo "\nERROR: SSH public key is not set for $THIS_USER..." + echo "\nTo Configure Run: ssh-keygen -t type" + echo "Where type is rsa or dsa encryption\n" + echo "Would you like to set up the keys now? (y/n): \c" + read REPLY + case $REPLY in + y|Y) if $(id $THIS_USER >/dev/null 2>&1) + then + echo "\nEncryption Type: (dsa or rsa?): \c" + read KEYTYPE + case "$KEYTYPE" in + +([d|Ds|Sa|A])) KEYTYPE=dsa + ;; + +([r|Rs|Sa|A])) KEYTYPE=rsa + ;; + *) echo "\nERROR: Invalid entry...Exiting..." + exit 1 + ;; + esac + echo "\nAccept the defaults and do not enter a passphrase...\n" + su - $THIS_USER "-c ssh-keygen -t $KEYTYPE" + if (( $? == 0 )) + then + echo "\nSuccess, keying $THIS_USER on $RHOST\n" + keyit_${KEYTYPE} $KEYTYPE + fi + else + echo "\nERROR: $THIS_USER username does not exist\n" + fi + ;; + *) # Do nothing + : # A colon, :, is a "no-op" + ;; + esac +fi + +######################################### +# END OF KEYIT SCRIPT +######################################### diff --git a/chapter16/proc_mon.ksh b/chapter16/proc_mon.ksh new file mode 100755 index 0000000..28365ba --- /dev/null +++ b/chapter16/proc_mon.ksh @@ -0,0 +1,152 @@ +#!/usr/bin/ksh +# +# SCRIPT: proc_mon.ksh +# AUTHOR: Randy Michael +# DATE: 02/14/2007 +# REV: 1.1.P +# PLATFORM: Not Platform Dependent +# +# PURPOSE: This script is used to monitor a process to end +# specified by ARG1 if a single command-line argument is +# used. There is also a "verbose" mode where the monitored +# process is displayed and ARG2 is monitored. +# +# USAGE: proc_mon.ksh [-v] process-to-monitor +# +# EXIT STATUS: +# 0 ==> Monitored process has terminated +# 1 ==> Script usage error +# 2 ==> Target process to monitor is not active +# 3 ==> This script exits on a trapped signal +# +# REV. LIST: +# +# 02/22/2007 - Added code for a "verbose" mode to output the +# results of the .ps aux. command. The verbose +# mode is set using a "-v" switch. +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to debug without any command execution + +SCRIPT_NAME=`basename $0` + +######################################################## +############ DEFINE FUNCTIONS HERE ##################### +######################################################## + +function usage +{ + echo "\n\n" + echo "USAGE: $SCRIPT_NAME [-v] {Process_to_monitor}" + echo "\nEXAMPLE: $SCRIPT_NAME my_backup\n" + echo "OR" + echo "\nEXAMPLE: $SCRIPT_NAME -v my_backup\n" + echo "Try again...EXITING...\n" +} +######################################################## + +function exit_trap +{ + echo "\n...EXITING on trapped signal...\n" +} +######################################################## +################ START OF MAIN########################## +######################################################## + +################ +# Set a trap...# +################ + +trap 'exit_trap; exit 3' 1 2 3 15 + +# First Check for the Correct Number of Arguments +# One or Two is acceptable + +if (( $# != 1 && $# != 2 )) +then + usage + exit 1 +fi + +# Parse through the command-line arguments and see if verbose +# mode has been specified. NOTICE that we assign the target +# process to the PROCESS variable!!! +# Embedded case statement... + +case $# in + 1) case $1 in + '-v') usage + exit 1 + ;; + *) PROCESS=$1 + ;; + esac + ;; + 2) case $1 in + '-v') continue + ;; + esac + + case $2 in + '-v') usage + exit 1 + ;; + *) PROCESS=$2 + ;; + esac + ;; + *) usage + exit 1 + ;; +esac + +# Check if the process is running or exit! + +ps aux | grep "$PROCESS" | grep -v "grep $PROCESS" \ +| grep -v $SCRIPT_NAME >/dev/null + +if (( $? != 0 )) +then + echo "\n\n$PROCESS is NOT an active process...EXITING...\n" + exit 2 +fi + +# Show verbose mode if specified... + +if (( $# == 2 )) && [[ $1 = "-v" ]] +then + # Verbose mode has been specified! + echo "\n" + + # Extract the columns heading from the ps aux output + ps aux | head -n 1 + + ps aux | grep "$PROCESS" | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME +fi + +##### O.K. The process is running, start monitoring... + +SLEEP_TIME="1" # Seconds between monitoring + +RC="0" # RC is the Return Code +echo "\n\n" # Give a couple of blank lines + +echo "$PROCESS is currently RUNNING...`date`\n" + +#################################### +# Loop UNTIL the $PROCESS stops... + +while (( RC == 0 )) # Loop until the return code is not zero +do + ps aux | grep $PROCESS | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME >/dev/null 2>&1 + if (( $? != 0 )) # Check the Return Code!!!!! + then + echo "\n...$PROCESS has COMPLETED...`date`\n" + exit 0 + fi + sleep $SLEEP_TIME # Needed to reduce CPU Load!!! +done + +# End of Script diff --git a/chapter16/proc_wait.ksh b/chapter16/proc_wait.ksh new file mode 100755 index 0000000..db1a85a --- /dev/null +++ b/chapter16/proc_wait.ksh @@ -0,0 +1,78 @@ +#!/usr/bin/ksh +# +# SCRIPT: proc_wait.ksh +# +# AUTHOR: Randy Michael +# +# DATE: 02/14/2007 +# +# REV: 1.1.A +# +# PURPOSE: This script is used to wait for a process to start. +# The process, specified by ARG 1 as passed to this script, should not +# currently be running when this is started. This script waits for the +# process to start and exits. +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to debug without any command execution + +################ FUNCTIONS ############################# +######################################################## +function usage +{ + echo "\n\n" + echo "USAGE: `basename $0` {Process_to_monitor}" + echo "\nEXAMPLE: `basename $0` bffcreate\n" + echo "Try again...EXITING...\n" +} +######################################################## +function exit_trap +{ + echo "\n...EXITING on trapped signal...\n" +} +######################################################## + + +################ START OF MAIN########################## + +if [ $# -ne 1 ] +then + usage + exit 1 +else + ARG1="$1" +fi + +# Set a trap... +trap 'exit_trap; exit 2' 1 2 3 15 + +# Check to execute or just exit... +ps -ef | grep $ARG1 | grep -v "grep $ARG1" | grep -v `basename $0` \ + >/dev/null + +if [ $? -eq 0 ] +then + echo "\n\n$ARG1 is an active process...EXITING...\n" + exit 1 +fi + +##### O.K. The process is NOT running, start monitoring for startup... + +SLEEP_TIME="1" # Seconds between monitoring +RC="1" # RC is the Return Code +echo "\n\n" # Give a couple of blank lines + +echo "WAITING for $ARG1 to start...`date`\n" + +until [ $RC -eq 0 ] # Loop until the return code is zero +do + ps -ef | grep $ARG1 | egrep -v "grep $ARG1" | grep -v `basename $0` \ + >/dev/null 2>&1 + RC="$?" + if [ $RC -eq 0 ] + then + echo "$ARG1 is RUNNING...`date`\n" + exit 0 + fi + sleep $SLEEP_TIME +done diff --git a/chapter16/proc_watch.ksh b/chapter16/proc_watch.ksh new file mode 100755 index 0000000..71b648f --- /dev/null +++ b/chapter16/proc_watch.ksh @@ -0,0 +1,151 @@ +#!/bin/ksh +# +# SCRIPT: proc_watch.ksh +# AUTHOR: Randy Michael +# DATE: 09-12-2007 +# REV: 1.0.P +# PLATFORM: Not Platform Dependent +# +# PURPOSE: This script is used to monitor and log +# the status of a process as it starts and stops. +# +# REV LIST: +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check syntax without ANY execution +# +#################################################### +########## DEFINE FILES AND VARIABLES HERE ######### +#################################################### + +LOGFILE="/tmp/proc_status.log" +[[ ! -s $LOGFILE ]] && touch $LOGFILE + +PROCESS="$1" # Process to Monitor +SCRIPT_NAME=$(basename $0) # Script Name w/o the PATH +TTY=$(tty) # Current tty or pty + +#################################################### +############# DEFINE FUNCTIONS HERE ################ +#################################################### + +usage () +{ + echo "\nUSAGE: $SCRIPT_NAME process_to_monitor\n" +} + +#################################################### + +trap_exit () +{ + # Log an ending time for process monitoring + TIMESTAMP=$(date +%D@%T) # Get a new timestamp... + echo "MON_STOP: Monitoring for $PROCESS ended ==> $TIMESTAMP" \ + | tee -a $LOGFILE + + # Kill all functions + kill -9 $(jobs -p) 2>/dev/null +} + +#################################################### + +mon_proc_end () +{ + END_RC="0" + until (( END_RC != 0 )) + do + ps aux | grep -v "grep $PROCESS" | grep -v $SCRIPT_NAME \ + | grep $PROCESS >/dev/null 2>&1 + + END_RC=$? # Check the Return Code!! + sleep 1 # Needed to reduce CPU load! + done + + echo 'N' # Turn the RUN flag off + + # Grab a Timestamp + TIMESTAMP=$(date +%D@%T) + + echo "END PROCESS: $PROCESS ended ==> $TIMESTAMP" >> $LOGFILE & + echo "END PROCESS: $PROCESS ended ==> $TIMESTAMP" > $TTY +} +#################################################### + +mon_proc_start () +{ + START_RC="-1" # Initialize to -1 + until (( START_RC == 0 )) + do + ps aux | grep -v "grep $PROCESS" | grep -v $SCRIPT_NAME \ + | grep $PROCESS >/dev/null 2>&1 + + START_RC=$? # Check the Return Code!!! + sleep 1 # Needed to reduce CPU load! + done + + echo 'Y' # Turn the RUN flag on + + # Grab the timestamp + TIMESTAMP=$(date +%D@%T) + + echo "START PROCESS: $PROCESS began ==> $TIMESTAMP" >> $LOGFILE & + echo "START PROCESS: $PROCESS began ==> $TIMESTAMP" > $TTY +} + +#################################################### +############## START OF MAIN ####################### +#################################################### + +### SET A TRAP #### + +trap 'trap_exit; exit 0' 1 2 3 15 + +# Check for the Correct Command Line Argument - Only 1 + +if (( $# != 1 )) +then + usage + exit 1 +fi + +# Get an Initial Process State and Set the RUN Flag + +ps aux | grep -v "grep $PROCESS" | grep -v $SCRIPT_NAME \ + | grep $PROCESS >/dev/null + +PROC_RC=$? # Check the Return Code!! + +# Give some initial feedback before starting the loop + +if (( PROC_RC == 0 )) +then + echo "The $PROCESS process is currently running...Monitoring..." + RUN="Y" # Set the RUN Flag to YES +else + echo "The $PROCESS process is not currently running...Monitoring..." + RUN="N" # Set the RUN Flag to NO +fi + +TIMESTAMP=$(date +%D@%T) # Grab a timestamp for the log + +# Use a "tee -a $#LOGFILE" to send output to both standard output +# and to the file referenced by $LOGFILE + +echo "MON_START: Monitoring for $PROCESS began ==> $TIMESTAMP" \ + | tee -a $LOGFILE + +# Loop Forever!! + +while : +do + case $RUN in + 'Y') # Loop Until the Process Ends + RUN=$(mon_proc_end) + ;; + 'N') # Loop Until the Process Starts + RUN=$(mon_proc_start) + ;; + esac +done + +# End of Script diff --git a/chapter16/proc_watch_timed.ksh b/chapter16/proc_watch_timed.ksh new file mode 100755 index 0000000..de61244 --- /dev/null +++ b/chapter16/proc_watch_timed.ksh @@ -0,0 +1,570 @@ +#!/bin/ksh +# +# SCRIPT: proc_watch_timed.ksh +# AUTHOR: Randy Michael +# DATE: 09-14-2007 +# REV: 1.0.P +# PLATFORM: Not Platform Dependent +# +# PURPOSE: This script is used to monitor and log +# the status of a process as it starts and stops. +# Command line options are used to identify the target +# process to monitor and the length of time to monitor. +# Each event is logged to the file defined by the +# $LOGFILE variable. This script also has the ability +# to execute pre, startup, and post events. These are +# controlled by the $RUN_PRE_EVENT, $RUN_STARTUP_EVENT, +# and $RUN_POST_EVENT variables. These variables control +# execution individually. Whatever is to be executed is to +# be placed in either the "pre_event_script", +# startup_event_script, or the +# "post_event_script" functions, or in any combination. +# Timing is controlled on the command line. +# +# USAGE: $SCRIPT_NAME total_seconds target_process +# +# Will monitor the specified process for the +# specified number of seconds. +# +# USAGE: $SCRIPT_NAME [-s|-S seconds] [-m|-M minutes] +# [-h|-H hours] [-d|-D days] +# [-p|-P process] +# +# Will monitor the specified process for number of +# seconds specified within -s seconds, -m minutes, +# -h hours, and -d days. Any combination of command +# switches can be used. +# +# REV LIST: +# +# set -x # Uncomment to debug this script + set -n # Uncomment to check syntax without ANY execution +# +#################################################### +########## DEFINE FILES AND VARIABLES HERE ######### +#################################################### + +typeset -u RUN_PRE_EVENT # Force to UPPERCASE +typeset -u RUN_STARTUP_EVENT # Force to UPPERCASE +typeset -u RUN_POST_EVENT # force to UPPERCASE + +RUN_PRE_EVENT='N' # A 'Y' will execute, anything else will not +RUN_STARTUP_EVENT='Y' # A 'Y' will execute, anything else will not +RUN_POST_EVENT='Y' # A 'Y' will execute, anything else will not + +LOGFILE="/tmp/proc_status.log" +[[ ! -s $LOGFILE ]] && touch $LOGFILE + +SCRIPT_NAME=$(basename $0) +TTY=$(tty) +INTERVAL="1" # Seconds between sampling +JOBS= + +#################################################### +############# DEFINE FUNCTIONS HERE ################ +#################################################### +usage () +{ +echo "\n\n\t*****USAGE ERROR*****" +echo "\n\nUSAGE: $SCRIPT_NAME seconds process" +echo "\nWill monitor the specified process for the" +echo "specified number of seconds." +echo "\nUSAGE: $SCRIPT_NAME [-s|-S seconds] [-m|-M minutes]" +echo " [-h|-H hours] [-d|-D days] [-p|-P process]\n" +echo "\nWill monitor the specified process for number of" +echo "seconds specified within -s seconds, -m minutes," +echo "-h hours and -d days. Any combination of command" +echo "switches can be used.\n" +echo "\nEXAMPLE: $SCRIPT_NAME 300 dtcalc" +echo "\n\nEXAMPLE: $SCRIPT_NAME -m 5 -p dtcalc" +echo "\nBoth examples will monitor the dtcalc process" +echo "for 5 minutes. Can specify days, hours, minutes" +echo "and seconds, using -d, -h, -m and -s\n\n" +} +#################################################### +trap_exit () +{ +# set -x # Uncommant to debug this function +# Log an ending time for process monitoring +echo "INTERRUPT: Program Received an Interrupt...EXITING..." > $TTY +echo "INTERRUPT: Program Received an Interrupt...EXITING..." >> $LOGFILE +TIMESTAMP=$(date +%D@%T) # Get a new timestamp... +echo "MON_STOPPED: Monitoring for $PROCESS ended ==> $TIMESTAMP\n" \ + >> $TTY +echo "MON_STOPPED: Monitoring for $PROCESS ended ==> $TIMESTAMP\n" \ + >> $LOGFILE +echo "LOGFILE: All Events are Logged ==> $LOGFILE \n" > $TTY + +# Kill all functions +JOBS=$(jobs -p) +if [[ ! -z $JOBS && $JOBS != '' && $JOBS != '0' ]] +then + kill $(jobs -p) 2>/dev/null 1>&2 +fi +return 2 +} +#################################################### +pre_event_script () +{ +# Put anything that you want to execute BEFORE the +# monitored process STARTS in this function + +: # No-OP - Needed as a placeholder for an empty function + # Comment Out the Above colon, ':' + +PRE_RC=$? +return $PRE_RC +} +#################################################### +startup_event_script () +{ +# Put anything that you want to execute WHEN, or AS, the +# monitored process STARTS in this function + +: # No-OP - Needed as a placeholder for an empty function + # Comment Out the Above colon, ':' + +STARTUP_RC=$? +return $STARTUP_RC +} +#################################################### +post_event_script () +{ +# Put anything that you want to execute AFTER the +# monitored process ENDS in this function + +: # No-OP - Need as a placeholder for an empty function + # Comment Out the Above colon, ':' + +POST_RC=$? +return $POST_RC +} +#################################################### +# This function is used to test character strings + +test_string () +{ +if (( $# != 1 )) +then + echo 'ERROR' + return +fi + +C_STRING=$1 + +# Test the character string for its composition + +case $C_STRING in + + +([0-9])) echo 'POS_INT' # Integer >= 0 + ;; + +([-0-9])) echo 'NEG_INT' # Integer < 0 + ;; + +([a-z])) echo 'LOW_CASE' # lower case text + ;; + +([A-Z])) echo 'UP_CASE' # UPPER case text + ;; + +([a-z]|[A-Z])) echo 'MIX_CASE' # MIxed CAse text + ;; + *) echo 'UNKNOWN' # Anything else + ;; +esac +} +#################################################### +proc_watch () +{ +# set -x # Uncomment to debug this function +# This function does all of the process monitoring! + +while : # Loop Forever!! +do + case $RUN in + 'Y') + # This will run the startup_event_script, which is a function + if [[ $RUN_STARTUP_EVENT = 'Y' ]] + then + echo "STARTUP EVENT: Executing Startup Event Script..."\ + > $TTY + echo "STARTUP EVENT: Executing Startup Event Script..."\ + >> $LOGFILE + + startup_event_script # USER DEFINED FUNCTION!!! + RC=$? # Check the Return Code!! + if (( "RC" == 0 )) + then + echo "SUCCESS: Startup Event Script Completed RC -\ +${RC}" > $TTY + echo "SUCCESS: Startup Event Script Completed RC -\ +${RC}" >> $LOGFILE + + else + echo "FAILURE: Startup Event Script FAILED RC -\ +${RC}" > $TTY + echo "FAILURE: Startup Event Script FAILED RC -\ +${RC}" >> $LOGFILE + fi + fi + integer PROC_COUNT='-1' # Reset the Counters + integer LAST_COUNT='-1' + # Loop until the process(es) end(s) + + until (( "PROC_COUNT" == 0 )) + do + # This function is a Co-Process. $BREAK checks to see if + # "Program Interrupt" has taken place. If so BREAK will + # be 'Y' and we exit both the loop and function. + + read BREAK + if [[ $BREAK = 'Y' ]] + then + return 3 + fi + + PROC_COUNT=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME \ + | grep $PROCESS | wc -l) >/dev/null 2>&1 + + if (( "LAST_COUNT" > 0 && "LAST_COUNT" != "PROC_COUNT" )) + then + # The Process Count has Changed... + TIMESTAMP=$(date +%D@%T) + # Get a list of the PID of all of the processes + PID_LIST=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME \ + | grep $PROCESS | awk '{print $2}') + + echo "PROCESS COUNT: $PROC_COUNT $PROCESS\ +Processes Running ==> $TIMESTAMP" >> $LOGFILE & + echo "PROCESS COUNT: $PROC_COUNT $PROCESS\ +Processes Running ==> $TIMESTAMP" > $TTY + echo ACTIVE PIDS: $PID_LIST >> $LOGFILE & +echo ACTIVE PIDS: $PID_LIST > $TTY + fi + LAST_COUNT=$PROC_COUNT + sleep $INTERVAL # Needed to reduce CPU load! + done + + RUN='N' # Turn the RUN Flag Off + TIMESTAMP=$(date +%D@%T) + echo "ENDING PROCESS: $PROCESS END time ==>\ +$TIMESTAMP" >> $LOGFILE & + echo "ENDING PROCESS: $PROCESS END time ==>\ +$TIMESTAMP" > $TTY + + # This will run the post_event_script, which is a function + + if [[ $RUN_POST_EVENT = 'Y' ]] + then + echo "POST EVENT: Executing Post Event Script..."\ + > $TTY + echo "POST EVENT: Executing Post Event Script..."\ + >> $LOGFILE & + + post_event_script # USER DEFINED FUNCTION!!! + integer RC=$? + if (( "RC" == 0 )) + then + echo "SUCCESS: Post Event Script Completed RC -\ +${RC}" > $TTY + echo "SUCCESS: Post Event Script Completed RC -\ +${RC}" >> $LOGFILE + else + echo "FAILURE: Post Event Script FAILED RC - ${RC}"\ + > $TTY + echo "FAILURE: Post Event Script FAILED RC - ${RC}"\ + >> $LOGFILE + fi + fi + ;; + + 'N') + # This will run the pre_event_script, which is a function + + if [[ $RUN_PRE_EVENT = 'Y' ]] + then + echo "PRE EVENT: Executing Pre Event Script..." > $TTY + echo "PRE EVENT: Executing Pre Event Script..." >> $LOGFILE + + pre_event_script # USER DEFINED FUNCTION!!! + RC=$? # Check the Return Code!!! + if (( "RC" == 0 )) + then + echo "SUCCESS: Pre Event Script Completed RC - ${RC}"\ + > $TTY + echo "SUCCESS: Pre Event Script Completed RC - ${RC}"\ + >> $LOGFILE + else + echo "FAILURE: Pre Event Script FAILED RC - ${RC}"\ + > $TTY + echo "FAILURE: Pre Event Script FAILED RC - ${RC}"\ + >> $LOGFILE + fi + fi + + echo "WAITING: Waiting for $PROCESS to \ +startup...Monitoring..." + + integer PROC_COUNT='-1' # Initialize to a fake value + + # Loop until at least one process starts + + until (( "PROC_COUNT" > 0 )) + do + # This is a Co-Process. This checks to see if a "Program + # Interrupt" has taken place. If so BREAK will be 'Y' and + # we exit both the loop and function + + read BREAK + if [[ $BREAK = 'Y' ]] + then + return 3 + fi + + PROC_COUNT=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME | grep $PROCESS | wc -l) \ + >/dev/null 2>&1 + + sleep $INTERVAL # Needed to reduce CPU load! + done + + RUN='Y' # Turn the RUN Flag On + + TIMESTAMP=$(date +%D@%T) + + PID_LIST=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME \ + | grep $PROCESS | awk '{print $2}') + + if (( "PROC_COUNT" == 1 )) + then + echo "START PROCESS: $PROCESS START time ==>\ +$TIMESTAMP" >> $LOGFILE & + echo ACTIVE PIDS: $PID_LIST >> $LOGFILE & + echo "START PROCESS: $PROCESS START time ==>\ +$TIMESTAMP" > $TTY + echo ACTIVE PIDS: $PID_LIST > $TTY + elif (( "PROC_COUNT" > 1 )) + then + echo "START PROCESS: $PROC_COUNT $PROCESS\ +Processes Started: START time ==> $TIMESTAMP" >> $LOGFILE & + echo ACTIVE PIDS: $PID_LIST >> $LOGFILE & + echo "START PROCESS: $PROC_COUNT $PROCESS\ +Processes Started: START time ==> $TIMESTAMP" > $TTY + echo ACTIVE PIDS: $PID_LIST > $TTY + fi + ;; + esac +done +} + +#################################################### +############## START OF MAIN ####################### +#################################################### + +### SET A TRAP #### + +trap 'BREAK='Y';print -p $BREAK 2>/dev/null;trap_exit\ +2>/dev/null;exit 0' 1 2 3 15 + +BREAK='N' # The BREAK variable is used in the co-process proc_watch +PROCESS= # Initialize to null +integer TOTAL_SECONDS=0 + +# Check commnand line arguments + +if (( $# > 10 || $# < 2 )) +then + usage + exit 1 +fi + +# Check to see if only the seconds and a process are +# the only arguments + +if [[ ($# -eq 2) && ($1 != -*) && ($2 != -*) ]] +then + NUM_TEST=$(test_string $1) # Is this an Integer? + if [[ "$NUM_TEST" = 'POS_INT' ]] + then + TOTAL_SECONDS=$1 # Yep - It.s an Integer + PROCESS=$2 # Can be anything + else + usage + exit 1 + fi +else + # Since getopts does not care what arguments it gets, let.s + # do a quick sanity check to make sure that we only have + # between 2 and 10 arguments and the first one must start + # with a -* (hyphen and anything), else usage error + + case "$#" in + [2-10]) if [[ $1 != -* ]]; then + usage; exit 1 + fi + ;; + esac + + HOURS=0 # Initialize all to zero + MINUTES=0 + SECS=0 + DAYS=0 + + # Use getopts to parse the command line arguments + # For each $OPTARG for DAYS, HOURS, MINUTES and DAYS check to see + # that each one is an integer by using the check_string function + + while getopts ":h:H:m:M:s:S:d:D:P:p:" OPT_LIST 2>/dev/null + do + case $OPT_LIST in + h|H) [[ $(test_string $OPTARG) != 'POS_INT' ]] && usage && exit 1 + (( HOURS = $OPTARG * 3600 )) # 3600 seconds per hour + ;; + m|H) [[ $(test_string $OPTARG) != 'POS_INT' ]] && usage && exit 1 + (( MINUTES = $OPTARG * 60 )) # 60 seconds per minute + ;; + s|S) [[ $(test_string $OPTARG) != 'POS_INT' ]] && usage && exit 1 + SECS="$OPTARG" # seconds are seconds + ;; + d|D) [[ $(test_string $OPTARG) != 'POS_INT' ]] && usage && exit 1 + (( DAYS = $OPTARG * 86400 )) # 86400 seconds per day + ;; + p|P) PROCESS=$OPTARG # process can be anything + ;; + \?) usage # USAGE ERROR + exit 1 + ;; + :) usage + exit 1 + ;; + *) usage + exit 1 + ;; + esac + done +fi + +# We need to make sure that we have a process that +# is NOT null or empty! - sanity check - The double quotes are required! + +if [[ -z "$PROCESS" || "$PROCESS" = '' ]] +then + usage + exit 1 +fi + +# Check to see that TOTAL_SECONDS was not previously set + +if (( TOTAL_SECONDS == 0 )) +then + # Add everything together if anything is > 0 + if [[ $SECS -gt 0 || $MINUTES -gt 0 || $HOURS -gt 0 \ + || $DAYS -gt 0 ]] + then + (( TOTAL_SECONDS = SECS + MINUTES + HOURS + DAYS )) + fi +fi + +# Last Sanity Check! + +if (( TOTAL_SECONDS <= 0 )) || [ -z $PROCESS ] +then + # Either There are No Seconds to Count or the + # $PROCESS Variable is Null...USAGE ERROR... + + usage + exit 1 +fi + +########### START MONITORING HERE!########### + +echo "\nCurrently running $PROCESS processes:\n" > $TTY +ps aux | grep -v "grep $PROCESS" | grep -v $SCRIPT_NAME \ + | grep $PROCESS > $TTY + +PROC_RC=$? # Get the initial state of the monitored function + +echo >$TTY # Send a blank line to the screen + +(( PROC_RC != 0 )) && echo "\nThere are no $PROCESS processes running\n" + +if (( PROC_RC == 0 )) # The Target Process(es) is/are running... +then + RUN='Y' # Set the RUN flag to true, or yes. + + integer PROC_COUNT # Strips out the "padding" for display + + PROC_COUNT=$(ps aux | grep -v "grep $PROCESS" | grep -v \ + $SCRIPT_NAME | grep $PROCESS | wc -l) >/dev/null 2>&1 + if (( PROC_COUNT == 1 )) + then + echo "The $PROCESS process is currently\ +running...Monitoring...\n" + elif (( PROC_COUNT > 1 )) + then + print "There are $PROC_COUNT $PROCESS processes currently\ +running...Monitoring...\n" + fi +else + echo "The $PROCESS process is not currently running...monitoring..." + RUN='N' # Set the RUN flag to false, or no. +fi + +TIMESTAMP=$(date +%D@%T) # Time that this script started monitoring + +# Get a list of the currently active process IDs + +PID_LIST=$(ps aux | grep -v "grep $PROCESS" \ + | grep -v $SCRIPT_NAME \ + | grep $PROCESS | awk '{print $2}') + +echo "MON_STARTED: Monitoring for $PROCESS began ==> $TIMESTAMP" \ + | tee -a $LOGFILE +echo ACTIVE PIDS: $PID_LIST | tee -a $LOGFILE + +##### NOTICE #### +# We kick off the "proc_watch" function below as a "Co-Process" +# This sets up a two way communications link between the +# "proc_watch" background function and this "MAIN BODY" of +# the script. This is needed because the function has two +# "infinite loops", with one always executing at any given time. +# Therefore we need a way to break out of the loop in case of +# an interrupt, i.e. CTRL+C, and when the countdown is complete. +# The "pipe appersand", |&, creates the background Co-Process +# and we use "print -p $VARIABLE" to transfer the variable.s +# value back to the background co-process. +################################### + +proc_watch |& # Create a Background Co-Process!! +WATCH_PID=$! # Get the process ID of the last background job! + +# Start the Count Down! + +integer SECONDS_LEFT=$TOTAL_SECONDS + +while (( SECONDS_LEFT > 0 )) +do + # Next send the current value of $BREAK to the Co-Process + # proc_watch, which was piped to the background... + + print -p $BREAK 2>/dev/null + (( SECONDS_LEFT = SECONDS_LEFT - 1 )) + sleep 1 # 1 Second Between Counts +done + +# Finished - Normal Timeout Exit... +TIMESTAMP=$(date +%D@%T) # Get a new timestamp... +echo "MON_STOPPED: Monitoring for $PROCESS ended ==> $TIMESTAMP\n" \ + | tee -a $LOGFILE + +echo "LOGFILE: All Events are Logged ==> $LOGFILE \n" + +# Tell the proc_watch function to break out of the loop and die +BREAK='Y' +print -p $BREAK 2>/dev/null + +kill $WATCH_PID 2>/dev/null + +exit 0 + +# End of Script diff --git a/chapter17/fs_mon_AIX.ksh b/chapter17/fs_mon_AIX.ksh new file mode 100755 index 0000000..98af9b4 --- /dev/null +++ b/chapter17/fs_mon_AIX.ksh @@ -0,0 +1,53 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_AIX.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 1.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: AIX +# +# REV LIST: +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +FSMAX="85" # Max. FS percentage value + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +THISHOST=`hostname` # Hostname of this machine + +######## START OF MAIN ############# + +# Get the data of interest by stripping out /dev/cd#, +# /proc rows and keeping columns 1, 4 and 7 + +df -k | tail +2 | egrep -v '/dev/cd[0-9]|/proc' \ + | awk '{print $1, $4, $7}' > $WORKFILE + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSVALUE FSMOUNT +do + FSVALUE=$(echo "$FSVALUE" | sed s/\%//g) # Remove the % sign + if [ "$FSVALUE" -gt "$FSMAX" ] + then + echo "$FSDEVICE mounted on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi +done < $WORKFILE + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_AIX_MBFREE.ksh b/chapter17/fs_mon_AIX_MBFREE.ksh new file mode 100755 index 0000000..dcc2ade --- /dev/null +++ b/chapter17/fs_mon_AIX_MBFREE.ksh @@ -0,0 +1,61 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_AIX_MBFREE.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 1.5.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: AIX +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +MIN_MB_FREE="50MB" # Min. MB of Free FS Space + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +THISHOST=`hostname` # Hostname of this machine + +######## START OF MAIN ############# + +# Get the data of interest by stripping out /dev/cd#, +# /proc rows and keeping columns 1, 4 and 7 + +df -k | tail +2 | egrep -v '/dev/cd[0-9]|/proc' \ + | awk '{print $1, $3, $7}' > $WORKFILE + +# Format Variables +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSMB_FREE FSMOUNT +do + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has \ +${FS_FREE_OUT}MB Free" >> $OUTFILE + fi + +done < $WORKFILE # Feed the while loop from the bottom!! + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_AIX_MBFREE_excep.ksh b/chapter17/fs_mon_AIX_MBFREE_excep.ksh new file mode 100755 index 0000000..d413805 --- /dev/null +++ b/chapter17/fs_mon_AIX_MBFREE_excep.ksh @@ -0,0 +1,131 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_AIX_MBFREE_excep.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 2.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: AIX +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# Randy Michael - 08-27-2007 +# Added code to allow you to override the set script default +# for MIN_MB_FREE of FS Space +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +MIN_MB_FREE="50MB" # Min. MB of Free FS Space + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +EXCEPTIONS="/usr/local/bin/exceptions" # Override data file +DATA_EXCEPTIONS="/tmp/dfdata.out" # Exceptions file w/o # rows +THISHOST=`hostname` # Hostname of this machine + +####### DEFINE FUNCTIONS HERE ######## + +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME FSLIMIT +do + # Do an NFS sanity check + echo $FSNAME | grep ":" >/dev/null \ + && FSNAME=$(echo $FSNAME | cut -d ":" -f2) + if [[ ! -z "$FSLIMIT" && "$FSLIMIT" != '' ]] # Check for empty/null + then + (( FSLIMIT = $(echo $FSLIMIT | sed s/MB//g) * 1024 )) + if [[ $FSNAME = $FSMOUNT ]] + then + # Get rid of the "MB" if it exists + FSLIMIT=$(echo $FSLIMIT | sed s/MB//g) + if (( $FSMB_FREE < $FSLIMIT )) + then + return 1 # Found out of limit + else + return 2 # Found OK + fi + fi + fi +done < $DATA_EXCEPTIONS # Feed the loop from the bottom!!! + +return 3 # Not found in $EXCEPTIONS file +} + +######## START OF MAIN ############# + +# Load the $EXCEPTIONS file if it exists + +if [[ -s $EXCEPTIONS ]] +then + # Ignore all lines beginning with a pound sign, # + # and omit all blank lines + cat $EXCEPTIONS | grep -v "^#" | sed /^$/d > $DATA_EXCEPTIONS +fi + +# Get the data of interest by stripping out /dev/cd#, +# /proc rows and keeping columns 1, 4 and 7 + +df -k | tail +2 | egrep -v '/dev/cd[0-9]|/proc' \ + | awk '{print $1, $3, $7}' > $WORKFILE + +# Format Variables for the proper MB value +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSMB_FREE FSMOUNT +do + if [[ -s $EXCEPTIONS ]] + then + check_exceptions + RC="$?" + if (( RC == 1 )) # Found out of exceptions limit + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" >> $OUTFILE + elif (( $RC == 2 )) # Found in exceptions to be OK + then # Just a sanity check - We really do nothing here... + # The colon, :, is a NO-OP operator in KSH + + : # No-Op - Do Nothing! + + elif [ $RC -eq 3 ] # Not found in the exceptions file + then + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" >> $OUTFILE + fi + fi + else # No Exceptions file use the script default + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + fi +done < $WORKFILE + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_AIX_PC_MBFREE.ksh b/chapter17/fs_mon_AIX_PC_MBFREE.ksh new file mode 100755 index 0000000..f002c99 --- /dev/null +++ b/chapter17/fs_mon_AIX_PC_MBFREE.ksh @@ -0,0 +1,280 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_AIX_PC_MBFREE.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 4.3.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the MAX_PERCENT value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: AIX +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# Randy Michael - 08-27-2007 +# Added code to allow you to override the set script default +# for MIN_MB_FREE of FS Space +# +# Randy Michael - 08-28-2007 +# Changed the code to handle both %Used and MB of Free Space. +# It does an "auto-detection" but has override capability +# of both the trigger level and the monitoring method using +# the exceptions file pointed to by the $EXCEPTIONS variable +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +EXCEPTIONS="/usr/local/bin/exceptions" # Override data file +DATA_EXCEPTIONS="/tmp/dfdata.out" # Exceptions file w/o # rows +EXCEPT_FILE="N" # Assume no $EXCEPTIONS FILE +THISHOST=`hostname` # Hostname of this machine + +MIN_MB_FREE="100MB" # Min. MB of Free FS Space +MAX_PERCENT="85%" # Max. FS percentage value +FSTRIGGER="1000MB" # Trigger to switch from % Used to MB Free + + +###### FORMAT VARIABLES HERE ###### + +# Both of these variables need to multiplied by 1024 blocks +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) +(( FSTRIGGER = $(echo $FSTRIGGER | sed s/MB//g) * 1024 )) + +####### DEFINE FUNCTIONS HERE ######## + +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME FSLIMIT +do + IN_FILE="N" + + # Do an NFS sanity check and get rid of any ":". + # If this is found it is actaully an error entry + # but we will try to resolve it. It will only + # work if it is an NFS cross mount to the same + # mount point on both machines. + echo $FSNAME | grep ':' >/dev/null \ + && FSNAME=$(echo $FSNAME | cut -d ':' -f2) + + # Check for empty and null variable + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + if [[ $FSNAME = $FSMOUNT ]] # Found it! + then + # Check for "MB" Characters...Set IN_FILE=MB + echo $FSLIMIT | grep MB >/dev/null && IN_FILE="MB" \ + && (( FSLIMIT = $(echo $FSLIMIT \ + | sed s/MB//g) * 1024 )) + # check for "%" Character...Set IN_FILE=PC, for % + echo $FSLIMIT | grep "%" >/dev/null && IN_FILE="PC" \ + && FSLIMIT=$(echo $FSLIMIT | sed s/\%//g) + + case $IN_FILE in + MB) + # Up-case the characters, if they exist + FSLIMIT=$(echo $FSLIMIT | tr '[a-z]' '[A-Z]') + # Get rid of the "MB" if it exists + FSLIMIT=$(echo $FSLIMIT | sed s/MB//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid filesystem "MB" limit + if (( $FSLIMIT >= 0 && $FSLIMIT < $FSSIZE )) + then + if (( $FSMB_FREE < $FSLIMIT )) + then + return 1 # Found out of limit using MB Free method + else + return 3 # Found OK + fi + else + echo "\nERROR: Invalid filesystem MAX for $FSMOUNT - $FSLIMIT" + echo " Exceptions file value must be less than or" + echo " equal to the size of the filesystem measured" + echo " in 1024 bytes\n" + fi + else + echo "\nERROR: Null value specified in excepeptions file" + echo " for the $FSMOUNT mount point.\n" + fi + ;; + PC) + # Strip out the % sign if it exists + PC_USED=$(echo $PC_USED | sed s/\%//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid percentage, i.e. 0-100 + if (( $FSLIMIT >= 0 && $FSLIMIT <= 100 )) + then + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + else + echo "\nERROR: Invalid percentage for $FSMOUNT - $FSLIMIT" + echo " Exceptions file values must be" + echo " between 0 and 100%\n" + fi + else + echo "\nERROR: Null value specified in excepeptions file" + echo " for the $FSMOUNT mount point.\n" + fi + ;; + N) + # Method Not Specified - Use Script Defaults + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + return 1 # Found out of limit using MB Free method + else + return 3 # Found OK + fi + else # This is a standard filesystem + PC_USED=$(echo $PC_USED | sed s/\%//g) # Remove the % + FSLIMIT=$(echo $FSLIMIT | sed s/\%//g) # Remove the % + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + fi + ;; + esac + fi + fi +done < $DATA_EXCEPTIONS # Feed the loop from the bottom!!! + +return 4 # Not found in $EXCEPTIONS file +} + +#################################### + +function display_output +{ +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi +} + +#################################### + +function load_EXCEPTIONS_data +{ +# Ingore any line that begins with a pound sign, # + +cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS +} + +#################################### + +function load_FS_data +{ + df -k | tail +2 | egrep -v '/dev/cd[0-9]|/proc' \ + | awk '{print $1, $2, $3, $4, $7}' > $WORKFILE +} + +#################################### +######### START OF MAIN ############ +#################################### + +load_FS_data + +# Do we have a non-zero size $EXCEPTIONS file? + +if [[ -s $EXCEPTIONS ]] +then # Found a non-empty $EXCEPTIONS file + + load_EXCEPTIONS_data + EXCEP_FILE="Y" +fi + +while read FSDEVICE FSSIZE FSMB_FREE PC_USED FSMOUNT +do + if [[ $EXCEP_FILE = "Y" ]] + then + check_exceptions + CE_RC="$?" # Check Exceptions Return Code (CE_RC) + + case $CE_RC in + 1) # Found exceeded in exceptions file by MB Method + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + ;; + 2) # Found exceeded in exceptions file by %Used method + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + ;; + 3) # Found OK in exceptions file + : # NO-OP Do Nothing + ;; + + 4) # Not found in exceptions file - Use Default Triggers + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + else # This is a standard filesystem + PC_USED=$(echo $PC_USED | sed s/\%//g) + MAX_PERCENT=$(echo $MAX_PERCENT | sed s/\%//g) + if (( $PC_USED > $MAX_PERCENT )) + then + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + ;; + esac + + + else # NO $EXECPTIONS FILE USE DEFAULT TRIGGER VALUES + + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem - Use MB Free Method + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + else # This is a standard filesystem - Use % Used Method + PC_USED=$(echo $PC_USED | sed s/\%//g) + MAX_PERCENT=$(echo $MAX_PERCENT | sed s/\%//g) + if (( $PC_USED > $MAX_PERCENT )) + then + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + fi +done < $WORKFILE + +display_output + +# End of Script diff --git a/chapter17/fs_mon_AIX_PC_MBFREE_excep.ksh b/chapter17/fs_mon_AIX_PC_MBFREE_excep.ksh new file mode 100755 index 0000000..f0a2e58 --- /dev/null +++ b/chapter17/fs_mon_AIX_PC_MBFREE_excep.ksh @@ -0,0 +1,287 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_AIX_PC_MBFREE_excep.ksh` +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 4.3.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the MAX_PERCENT value. +# A message is displayed for all "full" filesystems. +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# Randy Michael - 08-27-2007 +# Added code to allow you to override the set script default +# for MIN_MB_FREE of FS Space +# +# Randy Michael - 08-28-2007 +# Changed the code to handle both %Used and MB of Free Space. +# It does an "auto-detection" but has override capability +# of both the trigger level and the monitoring method using +# the exceptions file pointed to by the $EXCEPTIONS variable +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +MIN_MB_FREE="100MB" # Min. MB of Free FS Space +MAX_PERCENT="85%" # Max. FS percentage value +FSTRIGGER="1000MB" # Trigger to switch from % Used to MB Free + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +EXCEPTIONS="/usr/local/bin/exceptions" # Override data file +DATA_EXCEPTIONS="/tmp/dfdata.out" # Exceptions file w/o # rows +>$DATA_EXCEPTIONS +EXCEPT_FILE="N" # Assume no $EXCEPTIONS FILE +THISHOST=`hostname` # Hostname of this machine + +###### FORMAT VARIABLES HERE ###### + +# Both of these variables need to multiplied by 1024 blocks +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) +(( FSTRIGGER = $(echo $FSTRIGGER | sed s/MB//g) * 1024 )) + +####### DEFINE FUNCTIONS HERE ######## + +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME FSLIMIT +do + IN_FILE="N" + + # Do an NFS sanity check and get rid of any ":". + # If this is found it is actaully an error entry + # but we will try to resolve it. It will only + # work if it is an NFS cross mount to the same + # mount point on both machines. + echo $FSNAME | grep ':' >/dev/null \ + && FSNAME=$(echo $FSNAME | cut -d ':' -f2) + + # Check for empty and null variable + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + if [[ $FSNAME = $FSMOUNT ]] # Found it! + then + # Check for "MB" Characters...Set IN_FILE=MB + echo $FSLIMIT | grep MB >/dev/null && IN_FILE="MB" \ + && (( FSLIMIT = $(echo $FSLIMIT \ + | sed s/MB//g) * 1024 )) + # check for "%" Character...Set IN_FILE=PC, for % + echo $FSLIMIT | grep "%" >/dev/null && IN_FILE="PC" \ + && FSLIMIT=$(echo $FSLIMIT | sed s/\%//g) + + case $IN_FILE in + MB) # Use Megabytes of free space to test + # Up-case the characters, if they exist + FSLIMIT=$(echo $FSLIMIT | tr '[a-z]' '[A-Z]') + # Get rid of the "MB" if it exists + FSLIMIT=$(echo $FSLIMIT | sed s/MB//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid filesystem "MB" limit + if (( $FSLIMIT >= 0 && $FSLIMIT < $FSSIZE )) + then + if (( $FSMB_FREE < $FSLIMIT )) + then + return 1 # Found out of limit + # using MB Free method + else + return 3 # Found OK + fi + else + echo "\nERROR: Invalid filesystem MAX for $FSMOUNT - $FSLIMIT" + echo " Exceptions file value must be less than or" + echo " equal to the size of the filesystem measured" + echo " in 1024 bytes\n" + fi + else + echo "\nERROR: Null value specified in excepeptions file" + echo " for the $FSMOUNT mount point.\n" + fi + ;; + PC) + # Strip out the % sign if it exists + PC_USED=$(echo $PC_USED | sed s/\%//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid percentage, i.e. 0-100 + if (( $FSLIMIT >= 0 && $FSLIMIT <= 100 )) + then + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + else + echo "\nERROR: Invalid percentage for $FSMOUNT - $FSLIMIT" + echo " Exceptions file values must be" + echo " between 0 and 100%\n" + fi + else + echo "\nERROR: Null value specified in excepeptions file" + echo " for the $FSMOUNT mount point.\n" + fi + ;; + N) # Test type not specified in exception file, use default + + # Inform the user of the exceptions file error... + echo "\nERROR: Missing testing type in exceptions file" + echo " for the $FSMOUNT mount point. A \"%\" or" + echo " \"MB\" must be a suffix to the numerical" + echo " entry. Using script default values...\n" + + # Method Not Specified - Use Script Defaults + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + return 1 # Found out of limit using MB Free method + else + return 3 # Found OK + fi + else # This is a standard filesystem + PC_USED=$(echo $PC_USED | sed s/\%//g) # Remove the % + FSLIMIT=$(echo $FSLIMIT | sed s/\%//g) # Remove the % + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + fi + ;; + esac + fi + fi +done < $DATA_EXCEPTIONS # Feed the loop from the bottom!!! + +return 4 # Not found in $EXCEPTIONS file +} + +#################################### + +function display_output +{ +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi +} + +#################################### + +function load_EXCEPTIONS_data +{ +# Ingore any line that begins with a pound sign, # +# and omit all blank lines + +cat $EXCEPTIONS | grep -v "^#" | sed /^$/d > $DATA_EXCEPTIONS +} + +#################################### + +function load_FS_data +{ + df -k | tail +2 | egrep -v '/dev/cd[0-9] | /proc' \ + | awk '{print $1, $2, $3, $4, $7}' > $WORKFILE +} + +#################################### +######### START OF MAIN ############ +#################################### + +load_FS_data + +# Do we have a non-zero size $EXCEPTIONS file? + +if [[ -s $EXCEPTIONS ]] +then # Found a non-empty $EXCEPTIONS file + + load_EXCEPTIONS_data + EXCEP_FILE="Y" +fi + +while read FSDEVICE FSSIZE FSMB_FREE PC_USED FSMOUNT +do + if [[ $EXCEP_FILE = "Y" ]] + then + check_exceptions + CE_RC="$?" # Check Exceptions Return Code (CE_RC) + + case $CE_RC in + 1) # Found exceeded in exceptions file by MB Method + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + ;; + 2) # Found exceeded in exceptions file by %Used method + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + ;; + 3) # Found OK in exceptions file + : # NO-OP Do Nothing + ;; + 4) # Not found in exceptions file - Use Default Triggers + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem + # Remove the "MB", if it exists + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) + typeset -i FSMB_FREE + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + else # This is a standard filesystem + PC_USED=$(echo $PC_USED | sed s/\%//g) + MAX_PERCENT=$(echo $MAX_PERCENT | sed s/\%//g) + if (( $PC_USED > $MAX_PERCENT )) + then + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + ;; + esac + + else # NO $EXECPTIONS FILE USE DEFAULT TRIGGER VALUES + + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem - Use MB Free Method + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + else # This is a standard filesystem - Use % Used Method + PC_USED=$(echo $PC_USED | sed s/\%//g) + MAX_PERCENT=$(echo $MAX_PERCENT | sed s/\%//g) + if (( $PC_USED > $MAX_PERCENT )) + then + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + fi +done < $WORKFILE + +display_output + +# End of Script diff --git a/chapter17/fs_mon_AIX_excep.ksh b/chapter17/fs_mon_AIX_excep.ksh new file mode 100755 index 0000000..f22fa7a --- /dev/null +++ b/chapter17/fs_mon_AIX_excep.ksh @@ -0,0 +1,127 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_AIX_excep.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 2.1.P +# +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: AIX +# +# REV LIST: +# 08-23-2007 - Randy Michael +# Added code to override the default FSMAX script threshold +# using an "exceptions" file, defined by the $EXCEPTIONS +# variable, that list /mount_point and NEW_MAX% +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +FSMAX="85" # Max. FS percentage value + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +BINDIR="/usr/local/bin" # Local bin directory +THISHOST=`hostname` # Hostname of this machine + +EXCEPTIONS="${BINDIR}/exceptions" # Overrides $FSMAX +DATA_EXCEPTIONS="/tmp/dfdata.out" # Exceptions file w/o #, comments + +####### DEFINE FUNCTIONS HERE ##### + +function load_EXCEPTIONS_file +{ +# Ignore any line that begins with a pound sign, # +# and omit all blank lines + +cat $EXCEPTIONS | grep -v "^#" | sed /^$/d > $DATA_EXCEPTIONS +} + +################################### + +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME NEW_MAX # Feeding Ddata from Bottom of Loop!!! +do + if [[ $FSNAME = $FSMOUNT ]] # Correct /mount_point? + then # Get rid of the % sign, if it exists! + NEW_MAX=$(echo $NEW_MAX | sed s/\%//g) + + if [ $FSVALUE -gt $NEW_MAX ] + then # Over Limit...Return a "0", zero + return 0 # FOUND MAX OUT - Return 0 + fi + fi + +done < $DATA_EXCEPTIONS # Feed from the bottom of the loop!! + +return 1 # Not found in File +} + +#################################### +######## START OF MAIN ############# +#################################### + +# If there is an exceptions file...load it... + +[[ -s $EXCEPTIONS ]] && load_EXCEPTIONS_file + +# Get the data of interest by stripping out /dev/cd#, +# /proc rows and keeping columns 1, 4 and 7 + +df -k | tail +2 | egrep -v '/dev/cd[0-9]|/proc' \ + | awk '{print $1, $4, $7}' > $WORKFILE + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSVALUE FSMOUNT +do # Feeding the while loop from the BOTTOM!! + + # Strip out the % sign if it exists + FSVALUE=$(echo $FSVALUE | sed s/\%//g) # Remove the % sign + if [[ -s $EXCEPTIONS ]] # Do we have a non-empty file? + then # Found it! + + # Look for the current $FSMOUNT value in the file + # using the check_exceptions function defined above. + + check_exceptions + RC=$? # Get the return code from the function + if [ $RC -eq 0 ] # Found Exceeded in Exceptions File!! + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + elif [ $RC -eq 1 ] # Not found in exceptions, use defaults + then + if [ $FSVALUE -gt $FSMAX ] # Use Script Default + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi + fi + else # No exceptions file use the script default + if [ $FSVALUE -gt $FSMAX ] # Use Script Default + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi + fi +done < $WORKFILE # Feed the while loop from the bottom... + +# Display output if anything is exceeded... + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_ALL_OS.ksh b/chapter17/fs_mon_ALL_OS.ksh new file mode 100755 index 0000000..880c029 --- /dev/null +++ b/chapter17/fs_mon_ALL_OS.ksh @@ -0,0 +1,381 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_ALL_OS.ksh +# AUTHOR: Randy Michael +# DATE: 09-25-2007 +# REV: 6.0.P +# +# PURPOSE: This script is used to monitor for full filesystems, +# which are defined as .exceeding. the MAX_PERCENT value. +# A message is displayed for all .full. filesystems. +# +# PLATFORM: AIX, Linux, HP-UX, OpenBSD, and Solaris +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# Randy Michael - 08-27-2007 +# Added code to allow you to override the set script default +# for MIN_MB_FREE of FS Space +# +# Randy Michael - 08-28-2007 +# Changed the code to handle both %Used and MB of Free Space. +# It does an .auto-detection. but has override capability +# of both the trigger level and the monitoring method using +# the exceptions file pointed to by the $EXCEPTIONS variable +# +# Randy Michael - 08-28-2007 +# Added code to allow this script to be executed on +# AIX, Linux, HP-UX, and Solaris +# +# Randy Michael - 09=25=2007 +# Added code for OpenBSD support +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +MIN_MB_FREE="100MB" # Min. MB of Free FS Space +MAX_PERCENT="85%" # Max. FS percentage value +FSTRIGGER="1000MB" # Trigger to switch from % Used to MB Free + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +EXCEPTIONS="/usr/local/bin/exceptions" # Override data file +DATA_EXCEPTIONS="/tmp/dfdata.out" # Exceptions file w/o # rows +EXCEPT_FILE="N" # Assume no $EXCEPTIONS FILE +THISHOST=`hostname` # Hostname of this machine + +###### FORMAT VARIABLES HERE ###### + +# Both of these variables need to be multiplied by 1024 blocks +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) +(( FSTRIGGER = $(echo $FSTRIGGER | sed s/MB//g) * 1024 )) + +###### SHELL SPECIFIC VARIABLES ###### + +# Set the correct usage for the echo command for found $SHELL + +case $SHELL in +*/bin/bash) ECHO="echo -e" + ;; + */bin/ksh) ECHO="echo" + ;; + */bin/sh) ECHO="echo -e" + ;; + *) ECHO="echo" + ;; +esac + +###################################### +####### DEFINE FUNCTIONS HERE ######## +###################################### + +function get_OS_info +{ +# For a few commands it is necessary to know the OS and its level +# to execute the proper command syntax. This will always return +# the OS in UPPERCASE + +typeset -u OS # Use the UPPERCASE values for the OS variable +OS=`uname` # Grab the Operating system, i.e. AIX, HP-UX +print $OS # Send back the UPPERCASE value +} + +#################################### + +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME FSLIMIT +do + IN_FILE="N" + + # Do an NFS sanity check and get rid of any .:.. + # If this is found it is actually an error entry + # but we will try to resolve it. It will only + # work if it is an NFS cross mount to the same + # mount point on both machines. + $ECHO $FSNAME | grep ':' >/dev/null \ + && FSNAME=$($ECHO $FSNAME | cut -d ':' -f2) + + # Check for empty and null variable + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + if [[ $FSNAME = $FSMOUNT ]] # Found it! + then + # Check for "MB" Characters...Set IN_FILE=MB + $ECHO $FSLIMIT | grep MB >/dev/null && IN_FILE="MB" \ + && (( FSLIMIT = $($ECHO $FSLIMIT \ + | sed s/MB//g) * 1024 )) + # check for '%' Character...Set IN_FILE=PC, for % + $ECHO $FSLIMIT | grep '%' >/dev/null && IN_FILE="PC" \ + && FSLIMIT=$($ECHO $FSLIMIT | sed s/\%//g) + + case $IN_FILE in + MB) # Use MB of Free Space Method + # Up-case the characters, if they exist + FSLIMIT=$($ECHO $FSLIMIT | tr '[a-z]' '[A-Z]') + # Get rid of the 'MB' if it exists + FSLIMIT=$($ECHO $FSLIMIT | sed s/MB//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid filesystem 'MB' limit + if (( FSLIMIT >= 0 && FSLIMIT < FSSIZE )) + then + if (( FSMB_FREE < FSLIMIT )) + then + return 1 # Found out of limit + # using MB Free method + else + return 3 # Found OK + fi + else + $ECHO "\nERROR: Invalid filesystem MAX for\ + $FSMOUNT - $FSLIMIT" + $ECHO " Exceptions file value must be less\ + than or" + $ECHO " equal to the size of the filesystem\ + measured" + $ECHO " in 1024 bytes\n" + fi + else + $ECHO "\nERROR: Null value specified in exceptions\ + file" + $ECHO " for the $FSMOUNT mount point.\n" + fi + ;; + PC) # Use Filesystem %Used Method + # Strip out the % sign if it exists + PC_USED=$($ECHO $PC_USED | sed s/\%//g) + # Test for blank and null values + if [[ ! -z "$FSLIMIT" && "$FSLIMIT" != '' ]] + then + # Test for a valid percentage, i.e. 0-100 + if (( FSLIMIT >= 0 && FSLIMIT <= 100 )) + then + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + else + $ECHO "\nERROR: Invalid percentage for $FSMOUNT -\ + $FSLIMIT" + $ECHO " Exceptions file values must be" + $ECHO " between 0 and 100%\n" + fi + else + $ECHO "\nERROR: Null value specified in exceptions\ + file" + $ECHO " for the $FSMOUNT mount point.\n" + fi + ;; + N) # Method Not Specified - Use Script Defaults + if (( FSSIZE >= FSTRIGGER )) + then # This is a "large" filesystem + if (( FSMB_FREE < MIN_MB_FREE )) + then + return 1 # Found out of limit + # using MB Free method + else + return 3 # Found OK + fi + else # This is a standard filesystem + PC_USED=$($ECHO $PC_USED | sed s/\%//g) # Remove % + FSLIMIT=$($ECHO $FSLIMIT | sed s/\%//g) # Remove % + if (( PC_USED > FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + fi + ;; + esac + fi + fi +done < $DATA_EXCEPTIONS # Feed the loop from the bottom!!! + +return 4 # Not found in $EXCEPTIONS file +} + +#################################### + +function display_output +{ +if [[ -s $OUTFILE ]] +then + $ECHO "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi +} + +#################################### + +function load_EXCEPTIONS_data +{ +# Ignore any line that begins with a pound sign, # +# and omit all blank lines + +cat $EXCEPTIONS | grep -v "^#" | sed /^$/d > $DATA_EXCEPTIONS +} + +#################################### + +function load_AIX_FS_data +{ + + df -k | tail +2 | egrep -v "/dev/cd[0-9]|/proc" \ + | awk '{print $1, $2, $3, $4, $7}' > $WORKFILE +} + +#################################### + +function load_HP_UX_FS_data +{ + + bdf | tail +2 | egrep -v "/cdrom" \ + | awk '{print $1, $2, $4, $5, $6}' > $WORKFILE +} + +#################################### + +function load_LINUX_FS_data +{ + + df -k | tail -n +2 | egrep -v "/cdrom"\ + | awk '{print $1, $2, $4, $5, $6}' > $WORKFILE +} + +#################################### + +function load_OpenBSD_FS_data +{ + df -k | tail +2 | egrep -v "/mnt/cdrom"\ + | awk '{print $1, $2, $4, $5, $6}' > $WORKFILE +} + +#################################### + +function load_Solaris_FS_data +{ + + df -k | tail +2 | egrep -v "/dev/fd|/etc/mnttab|/proc"\ + | awk '{print $1, $2, $4, $5, $6}' > $WORKFILE +} + +#################################### +######### START OF MAIN ############ +#################################### + +# Query the operating system to find the Unix flavor, then +# load the correct filesystem data for the resident OS + +case $(get_OS_info) in + AIX) # Load filesystem data for AIX + load_AIX_FS_data + ;; + HP-UX) # Load filesystem data for HP-UX + load_HP_UX_FS_data + ;; + LINUX) # Load filesystem data for Linux + load_LINUX_FS_data + ;; + OPENBSD) # Load filesystem data for OpenBSD + load_OpenBSD_FS_data + ;; + SUNOS) # Load filesystem data for Solaris + load_Solaris_FS_data + ;; + *) # Unsupported in script + $ECHO "\nUnsupported Operating System for this Script...EXITING\n" + exit 1 +esac + +# Do we have a nonzero size $EXCEPTIONS file? + +if [[ -s $EXCEPTIONS ]] +then # Found a nonempty $EXCEPTIONS file + + load_EXCEPTIONS_data + EXCEP_FILE="Y" +fi + +while read FSDEVICE FSSIZE FSMB_FREE PC_USED FSMOUNT +do + if [[ $EXCEP_FILE = "Y" ]] + then + check_exceptions + CE_RC="$?" # Check Exceptions Return Code (CE_RC) + + case $CE_RC in + 1) # Found exceeded in exceptions file by MB Method + (( FS_FREE_OUT = FSMB_FREE / 1000 )) + $ECHO "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" >> $OUTFILE + ;; + 2) # Found exceeded in exceptions file by %Used method + $ECHO "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + ;; + 3) # Found OK in exceptions file + : # NO-OP Do Nothing. A ':' is a no-op! + ;; + + 4) # Not found in exceptions file - Use Default Triggers + if (( FSSIZE >= FSTRIGGER )) + then # This is a "large" filesystem + FSMB_FREE=$($ECHO $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( FSMB_FREE < MIN_MB_FREE )) + then + (( FS_FREE_OUT = FSMB_FREE / 1000 )) + $ECHO "$FSDEVICE mounted on $FSMOUNT has {FS_FREE_OUT}MB Free" >> $OUTFILE + fi + else # This is a standard filesystem + PC_USED=$($ECHO $PC_USED | sed s/\%//g) + MAX_PERCENT=$($ECHO $MAX_PERCENT | sed s/\%//g) + if (( PC_USED > MAX_PERCENT )) + then + $ECHO "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + ;; + esac + + else # NO $EXCEPTIONS FILE USE DEFAULT TRIGGER VALUES + + if (( FSSIZE >= FSTRIGGER )) + then # This is a "large" filesystem - Use MB Free Method + FSMB_FREE=$($ECHO $FSMB_FREE | sed s/MB//g) # Remove the 'MB' + if (( FSMB_FREE < MIN_MB_FREE )) + then + (( FS_FREE_OUT = FSMB_FREE / 1000 )) + $ECHO "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + else # This is a standard filesystem - Use % Used Method + PC_USED=$($ECHO $PC_USED | sed s/\%//g) + MAX_PERCENT=$($ECHO $MAX_PERCENT | sed s/\%//g) + if (( PC_USED > MAX_PERCENT )) + then + $ECHO "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + fi +done < $WORKFILE # Feed the while loop from the bottom!!!!! + +display_output + +# End of Script + diff --git a/chapter17/fs_mon_COMBO.ksh b/chapter17/fs_mon_COMBO.ksh new file mode 100755 index 0000000..f43f762 --- /dev/null +++ b/chapter17/fs_mon_COMBO.ksh @@ -0,0 +1,278 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 4.3.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the MAX_PERCENT value. +# A message is displayed for all "full" filesystems. +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# Randy Michael - 08-27-2007 +# Added code to allow you to override the set script default +# for MIN_MB_FREE of FS Space +# +# Randy Michael - 08-28-2007 +# Changed the code to handle both %Used and MB of Free Space. +# It does an "auto-detection" but has override capability +# of both the trigger level and the monitoring method using +# the exceptions file pointed to by the $EXCEPTIONS variable +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +EXCEPTIONS="/usr/local/bin/exceptions" # Override data file +DATA_EXCEPTIONS="/tmp/dfdata.out" # Exceptions file w/o # rows +EXCEPT_FILE="N" # Assume no $EXCEPTIONS FILE +THISHOST=`hostname` # Hostname of this machine + +MIN_MB_FREE="100MB" # Min. MB of Free FS Space +MAX_PERCENT="85%" # Max. FS percentage value +FSTRIGGER="1000MB" # Trigger to switch from % Used to MB Free + + +###### FORMAT VARIABLES HERE ###### + +# Both of these variables need to multiplied by 1024 blocks +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) +(( FSTRIGGER = $(echo $FSTRIGGER | sed s/MB//g) * 1024 )) + +####### DEFINE FUNCTIONS HERE ######## + +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME FSLIMIT +do + IN_FILE="N" + + # Do an NFS sanity check and get rid of any ":". + # If this is found it is actaully an error entry + # but we will try to resolve it. It will only + # work if it is an NFS cross mount to the same + # mount point on both machines. + echo $FSNAME | grep ':' >/dev/null \ + && FSNAME=$(echo $FSNAME | cut -d ':' -f2) + + # Check for empty and null variable + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + if [[ $FSNAME = $FSMOUNT ]] # Found it! + then + # Check for "MB" Characters...Set IN_FILE=MB + echo $FSLIMIT | grep MB >/dev/null && IN_FILE="MB" \ + && (( FSLIMIT = $(echo $FSLIMIT \ + | sed s/MB//g) * 1024 )) + # check for "%" Character...Set IN_FILE=PC, for % + echo $FSLIMIT | grep "%" >/dev/null && IN_FILE="PC" \ + && FSLIMIT=$(echo $FSLIMIT | sed s/\%//g) + + case $IN_FILE in + MB) + # Up-case the characters, if they exist + FSLIMIT=$(echo $FSLIMIT | tr '[a-z]' '[A-Z]') + # Get rid of the "MB" if it exists + FSLIMIT=$(echo $FSLIMIT | sed s/MB//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid filesystem "MB" limit + if (( $FSLIMIT >= 0 && $FSLIMIT < $FSSIZE )) + then + if (( $FSMB_FREE < $FSLIMIT )) + then + return 1 # Found out of limit using MB Free method + else + return 3 # Found OK + fi + else + echo "\nERROR: Invalid filesystem MAX for $FSMOUNT - $FSLIMIT" + echo " Exceptions file value must be less than or" + echo " equal to the size of the filesystem measured" + echo " in 1024 bytes\n" + fi + else + echo "\nERROR: Null value specified in excepeptions file" + echo " for the $FSMOUNT mount point.\n" + fi + ;; + PC) + # Strip out the % sign if it exists + PC_USED=$(echo $PC_USED | sed s/\%//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid percentage, i.e. 0-100 + if (( $FSLIMIT >= 0 && $FSLIMIT <= 100 )) + then + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + else + echo "\nERROR: Invalid percentage for $FSMOUNT - $FSLIMIT" + echo " Exceptions file values must be" + echo " between 0 and 100%\n" + fi + else + echo "\nERROR: Null value specified in excepeptions file" + echo " for the $FSMOUNT mount point.\n" + fi + ;; + N) + # Method Not Specified - Use Script Defaults + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + return 1 # Found out of limit using MB Free method + else + return 3 # Found OK + fi + else # This is a standard filesystem + PC_USED=$(echo $PC_USED | sed s/\%//g) # Remove the % + FSLIMIT=$(echo $FSLIMIT | sed s/\%//g) # Remove the % + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + fi + ;; + esac + fi + fi +done < $DATA_EXCEPTIONS # Feed the loop from the bottom!!! + +return 4 # Not found in $EXCEPTIONS file +} + +#################################### + +function display_output +{ +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi +} + +#################################### + +function load_EXCEPTIONS_data +{ +# Ingore any line that begins with a pound sign, # + +cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS +} + +#################################### + +function load_FS_data +{ + df -k | tail +2 | egrep -v '/dev/cd[0-9] | /proc' \ + | awk '{print $1, $2, $3, $4, $7}' > $WORKFILE +} + +#################################### +######### START OF MAIN ############ +#################################### + +load_FS_data + +# Do we have a non-zero size $EXCEPTIONS file? + +if [[ -s $EXCEPTIONS ]] +then # Found a non-empty $EXCEPTIONS file + + load_EXCEPTIONS_data + EXCEP_FILE="Y" +fi + +while read FSDEVICE FSSIZE FSMB_FREE PC_USED FSMOUNT +do + if [[ $EXCEP_FILE = "Y" ]] + then + check_exceptions + CE_RC="$?" # Check Exceptions Return Code (CE_RC) + + case $CE_RC in + 1) # Found exceeded in exceptions file by MB Method + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + ;; + 2) # Found exceeded in exceptions file by %Used method + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + ;; + 3) # Found OK in exceptions file + : # NO-OP Do Nothing + ;; + + 4) # Not found in exceptions file - Use Default Triggers + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + else # This is a standard filesystem + PC_USED=$(echo $PC_USED | sed s/\%//g) + MAX_PERCENT=$(echo $MAX_PERCENT | sed s/\%//g) + if (( $PC_USED > $MAX_PERCENT )) + then + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + ;; + esac + + + else # NO $EXECPTIONS FILE USE DEFAULT TRIGGER VALUES + + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem - Use MB Free Method + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + else # This is a standard filesystem - Use % Used Method + PC_USED=$(echo $PC_USED | sed s/\%//g) + MAX_PERCENT=$(echo $MAX_PERCENT | sed s/\%//g) + if (( $PC_USED > $MAX_PERCENT )) + then + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + fi +done < $WORKFILE + +display_output + +# End of Script diff --git a/chapter17/fs_mon_HPUX.ksh b/chapter17/fs_mon_HPUX.ksh new file mode 100755 index 0000000..f880e36 --- /dev/null +++ b/chapter17/fs_mon_HPUX.ksh @@ -0,0 +1,52 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_HPUX.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 1.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# REV LIST: +# +# PLATFORM: HP-UX +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +THISHOST=`hostname` # Hostname of this machine +FSMAX="85" # Max. FS percentage value + +######## START OF MAIN ############# + +# Get the data of interest by stripping out the +# /cdrom row and keeping columns 1, 5 and 6 + +bdf | tail +2 | egrep -v '/cdrom' \ + | awk '{print $1, $5, $6}' > $WORKFILE + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSVALUE FSMOUNT +do + FSVALUE=$(echo $FSVALUE | sed s/\%//g) # Remove the % sign + if [ $FSVALUE -gt $FSMAX ] + then + echo "$FSDEVICE mounted on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi +done < $WORKFILE + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_HPUX_MBFREE.ksh b/chapter17/fs_mon_HPUX_MBFREE.ksh new file mode 100755 index 0000000..7adb179 --- /dev/null +++ b/chapter17/fs_mon_HPUX_MBFREE.ksh @@ -0,0 +1,59 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_HPUX_MBFREE.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 1.5.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: HP-UX +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +THISHOST=`hostname` # Hostname of this machine +MIN_MB_FREE="50MB" # Min. MB of Free FS Space + +######## START OF MAIN ############# + +# Get the data of interest by stripping out the +# /cdrom row and keeping columns 1, 4 and 6 + +bdf | tail +2 | egrep -v '/cdrom' \ + | awk '{print $1, $4, $6}' > $WORKFILE + +# Format Variables +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSMB_FREE FSMOUNT +do + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi +done < $WORKFILE + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_HPUX_MBFREE_excep.ksh b/chapter17/fs_mon_HPUX_MBFREE_excep.ksh new file mode 100755 index 0000000..b3c86f9 --- /dev/null +++ b/chapter17/fs_mon_HPUX_MBFREE_excep.ksh @@ -0,0 +1,129 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_HPUX_MBFREE_excep.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 2.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: HP-UX +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# Randy Michael - 08-27-2007 +# Added code to allow you to override the set script default +# for MIN_MB_FREE of FS Space +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +EXCEPTIONS="/usr/local/bin/exceptions" # Override data file +DATA_EXCEPTIONS="/tmp/dfdata.out" # Exceptions file w/o # rows +THISHOST=`hostname` # Hostname of this machine +MIN_MB_FREE="50MB" # Min. MB of Free FS Space + +####### DEFINE FUNCTIONS HERE ######## + +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME FSLIMIT +do + # Do an NFS sanity check + echo $FSNAME | grep ":" >/dev/null \ + && FSNAME=$(echo $FSNAME | cut -d ":" -f2) + if [[ ! -z "$FSLIMIT" && "$FSLIMIT" != '' ]] + then + (( FSLIMIT = $(echo $FSLIMIT | sed s/MB//g) * 1024 )) + if [[ $FSNAME = $FSMOUNT ]] + then + # Get rid of the "MB" if it exists + FSLIMIT=$(echo $FSLIMIT | sed s/MB//g) + if (( $FSMB_FREE < $FSLIMIT )) + then + return 1 # Found out of limit + else + return 2 # Found OK + fi + fi + fi +done < $DATA_EXCEPTIONS # Feed the loop from the bottom!!! + +return 3 # Not found in $EXCEPTIONS file +} + +######## START OF MAIN ############# + +if [[ -s $EXCEPTIONS ]] +then + # Ignore all line beginning with a pound sign, #. + cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS +fi + +# Get the data of interest by stripping out the +# /cdrom row and keeping columns 1, 4 and 6 + +bdf | tail +2 | egrep -v '/cdrom' \ + | awk '{print $1, $4, $6}' > $WORKFILE + +# Format Variables for the proper MB value +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSMB_FREE FSMOUNT +do + if [[ -s $EXCEPTIONS ]] + then + check_exceptions + RC="$?" + if [ $RC -eq 1 ] # Found out of exceptions limit + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + elif [ $RC -eq 2 ] # Found in exceptions to be OK + then # Just a sanity check - We really do nothing here... + # The colon, :, is a NO-OP operator in KSH + + : # No-Op - Do Nothing! + + elif [ $RC -eq 3 ] # Not found in the exceptions file + then + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + fi + else # No Exceptions file use the script default + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + fi +done < $WORKFILE + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_HPUX_PC_MBFREE.ksh b/chapter17/fs_mon_HPUX_PC_MBFREE.ksh new file mode 100755 index 0000000..a996d0e --- /dev/null +++ b/chapter17/fs_mon_HPUX_PC_MBFREE.ksh @@ -0,0 +1,280 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_HPUX_PC_MBFREE.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 4.3.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the MAX_PERCENT value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: HP-UX +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# Randy Michael - 08-27-2007 +# Added code to allow you to override the set script default +# for MIN_MB_FREE of FS Space +# +# Randy Michael - 08-28-2007 +# Changed the code to handle both %Used and MB of Free Space. +# It does an "auto-detection" but has override capability +# of both the trigger level and the monitoring method using +# the exceptions file pointed to by the $EXCEPTIONS variable +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +EXCEPTIONS="/usr/local/bin/exceptions" # Override data file +DATA_EXCEPTIONS="/tmp/dfdata.out" # Exceptions file w/o # rows +EXCEPT_FILE="N" # Assume no $EXCEPTIONS FILE +THISHOST=`hostname` # Hostname of this machine + +MIN_MB_FREE="100MB" # Min. MB of Free FS Space +MAX_PERCENT="85%" # Max. FS percentage value +FSTRIGGER="1000MB" # Trigger to switch from % Used to MB Free + + +###### FORMAT VARIABLES HERE ###### + +# Both of these variables need to multiplied by 1024 blocks +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) +(( FSTRIGGER = $(echo $FSTRIGGER | sed s/MB//g) * 1024 )) + +####### DEFINE FUNCTIONS HERE ######## + +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME FSLIMIT +do + IN_FILE="N" + + # Do an NFS sanity check and get rid of any ":". + # If this is found it is actaully an error entry + # but we will try to resolve it. It will only + # work if it is an NFS cross mount to the same + # mount point on both machines. + echo $FSNAME | grep ':' >/dev/null \ + && FSNAME=$(echo $FSNAME | cut -d ':' -f2) + + # Check for empty and null variable + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + if [[ $FSNAME = $FSMOUNT ]] # Found it! + then + # Check for "MB" Characters...Set IN_FILE=MB + echo $FSLIMIT | grep MB >/dev/null && IN_FILE="MB" \ + && (( FSLIMIT = $(echo $FSLIMIT \ + | sed s/MB//g) * 1024 )) + # check for "%" Character...Set IN_FILE=PC, for % + echo $FSLIMIT | grep "%" >/dev/null && IN_FILE="PC" \ + && FSLIMIT=$(echo $FSLIMIT | sed s/\%//g) + + case $IN_FILE in + MB) + # Up-case the characters, if they exist + FSLIMIT=$(echo $FSLIMIT | tr '[a-z]' '[A-Z]') + # Get rid of the "MB" if it exists + FSLIMIT=$(echo $FSLIMIT | sed s/MB//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid filesystem "MB" limit + if (( $FSLIMIT >= 0 && $FSLIMIT < $FSSIZE )) + then + if (( $FSMB_FREE < $FSLIMIT )) + then + return 1 # Found out of limit using MB Free method + else + return 3 # Found OK + fi + else + echo "\nERROR: Invalid filesystem MAX for $FSMOUNT - $FSLIMIT" + echo " Exceptions file value must be less than or" + echo " equal to the size of the filesystem measured" + echo " in 1024 bytes\n" + fi + else + echo "\nERROR: Null value specified in excepeptions file" + echo " for the $FSMOUNT mount point.\n" + fi + ;; + PC) + # Strip out the % sign if it exists + PC_USED=$(echo $PC_USED | sed s/\%//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid percentage, i.e. 0-100 + if (( $FSLIMIT >= 0 && $FSLIMIT <= 100 )) + then + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + else + echo "\nERROR: Invalid percentage for $FSMOUNT - $FSLIMIT" + echo " Exceptions file values must be" + echo " between 0 and 100%\n" + fi + else + echo "\nERROR: Null value specified in excepeptions file" + echo " for the $FSMOUNT mount point.\n" + fi + ;; + N) + # Method Not Specified - Use Script Defaults + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + return 1 # Found out of limit using MB Free method + else + return 3 # Found OK + fi + else # This is a standard filesystem + PC_USED=$(echo $PC_USED | sed s/\%//g) # Remove the % + FSLIMIT=$(echo $FSLIMIT | sed s/\%//g) # Remove the % + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + fi + ;; + esac + fi + fi +done < $DATA_EXCEPTIONS # Feed the loop from the bottom!!! + +return 4 # Not found in $EXCEPTIONS file +} + +#################################### + +function display_output +{ +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi +} + +#################################### + +function load_EXCEPTIONS_data +{ +# Ingore any line that begins with a pound sign, # + +cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS +} + +#################################### + +function load_FS_data +{ + bdf | tail +2 | egrep -v '/cdrom' \ + | awk '{print $1, $2, $4, $5, $6}' > $WORKFILE +} + +#################################### +######### START OF MAIN ############ +#################################### + +load_FS_data + +# Do we have a non-zero size $EXCEPTIONS file? + +if [[ -s $EXCEPTIONS ]] +then # Found a non-empty $EXCEPTIONS file + + load_EXCEPTIONS_data + EXCEP_FILE="Y" +fi + +while read FSDEVICE FSSIZE FSMB_FREE PC_USED FSMOUNT +do + if [[ $EXCEP_FILE = "Y" ]] + then + check_exceptions + CE_RC="$?" # Check Exceptions Return Code (CE_RC) + + case $CE_RC in + 1) # Found exceeded in exceptions file by MB Method + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + ;; + 2) # Found exceeded in exceptions file by %Used method + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + ;; + 3) # Found OK in exceptions file + : # NO-OP Do Nothing + ;; + + 4) # Not found in exceptions file - Use Default Triggers + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + else # This is a standard filesystem + PC_USED=$(echo $PC_USED | sed s/\%//g) + MAX_PERCENT=$(echo $MAX_PERCENT | sed s/\%//g) + if (( $PC_USED > $MAX_PERCENT )) + then + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + ;; + esac + + + else # NO $EXECPTIONS FILE USE DEFAULT TRIGGER VALUES + + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem - Use MB Free Method + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + else # This is a standard filesystem - Use % Used Method + PC_USED=$(echo $PC_USED | sed s/\%//g) + MAX_PERCENT=$(echo $MAX_PERCENT | sed s/\%//g) + if (( $PC_USED > $MAX_PERCENT )) + then + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + fi +done < $WORKFILE + +display_output + +# End of Script diff --git a/chapter17/fs_mon_HPUX_excep.ksh b/chapter17/fs_mon_HPUX_excep.ksh new file mode 100755 index 0000000..332791b --- /dev/null +++ b/chapter17/fs_mon_HPUX_excep.ksh @@ -0,0 +1,113 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_HPUX_excep.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 2.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: HP-UX +# +# REV LIST: +# 08-23-2007 - Randy Michael +# Added code to override the default FSMAX script threshold +# using an "exceptions" file, defined by the $EXCEPTIONS +# variable, that list /mount_point and NEW_MAX% +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +BINDIR="/usr/local/bin" # Local bin directory +THISHOST=`hostname` # Hostname of this machine +FSMAX="85" # Max. FS percentage value +EXCEPTIONS="${BINDIR}/exceptions" # Overrides $FSMAX + +####### DEFINE FUNCTIONS HERE ##### + +function check_exceptions +{ + +# set -x # Uncomment to debug this function + +# Define a data file + +DATA_EXCEPTIONS="/tmp/dfdata.out" + +# Ingore any line that begins with a pound sign, # + +cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS + +while read FSNAME NEW_MAX # Feeding Ddata from Bottom of Loop!!! +do + if [[ $FSNAME = $FSMOUNT ]] # Correct /mount_point? + then # Get rid of the % sign, if it exists! + NEW_MAX=$(echo $NEW_MAX | sed s/\%//g) + + if [ $FSVALUE -gt $NEW_MAX ] + then # Over Limit...Return a "0", zero + return 0 # FOUND MAX OUT - Return 0 + fi + fi + +done < $DATA_EXCEPTIONS # Feed from the bottom of the loop!! + +return 1 # Not found in File +} + +######## START OF MAIN ############# + +# Get the data of interest by stripping out the +# /cdrom row and keeping columns 1, 5 and 6 + +bdf | tail +2 | egrep -v '/cdrom' \ + | awk '{print $1, $5, $6}' > $WORKFILE + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSVALUE FSMOUNT +do + # Strip out the % sign if it exists + FSVALUE=$(echo $FSVALUE | sed s/\%//g) # Remove the % sign + if [[ -s $EXCEPTIONS ]] # Do we have a non-empty file? + then # Found it! + + # Look for the current $FSMOUNT value in the file + # using the check_exceptions function defined above. + + check_exceptions + if [ $? -eq 0 ] # Found it Exceeded!! + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + else # Not exceeded in the file use the script default + if [ $FSVALUE -gt $FSMAX ] # Use Script Default + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi + fi + else # No exceptions file use the script default + if [ $FSVALUE -gt $FSMAX ] # Use Script Default + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi + fi +done < $WORKFILE # Feed the while loop from the bottom... + +# Display output if anything is exceeded... + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_LINUX.ksh b/chapter17/fs_mon_LINUX.ksh new file mode 100755 index 0000000..4a26a18 --- /dev/null +++ b/chapter17/fs_mon_LINUX.ksh @@ -0,0 +1,52 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_LINUX.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 1.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: Linux +# +# REV LIST: +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +THISHOST=`hostname` # Hostname of this machine +FSMAX="85" # Max. FS percentage value + +######## START OF MAIN ############# + +# Get the data of interest by stripping out the rows that +# are not monitored and keeping columns 1, 5 and 6 + +df -k | tail +2 | egrep -v '/cdrom' \ + | awk '{print $1, $5, $6}' > $WORKFILE + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSVALUE FSMOUNT +do + FSVALUE=$(echo $FSVALUE | sed s/\%//g) # Remove the % sign + if [ $FSVALUE -gt $FSMAX ] + then + echo "$FSDEVICE mounted on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi +done < $WORKFILE + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_LINUX_MBFREE.ksh b/chapter17/fs_mon_LINUX_MBFREE.ksh new file mode 100755 index 0000000..0582141 --- /dev/null +++ b/chapter17/fs_mon_LINUX_MBFREE.ksh @@ -0,0 +1,59 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_LINUX_MBFREE.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 1.5.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: Linux +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +THISHOST=`hostname` # Hostname of this machine +MIN_MB_FREE="50MB" # Min. MB of Free FS Space + +######## START OF MAIN ############# + +# Get the data of interest by stripping out the +# /cdrom row and keeping columns 1, 4 and 6 + +df -k | tail +2 | egrep -v '/cdrom' \ + | awk '{print $1, $4, $6}' > $WORKFILE + +# Format Variables +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSMB_FREE FSMOUNT +do + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi +done < $WORKFILE + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_LINUX_MBFREE_excep.ksh b/chapter17/fs_mon_LINUX_MBFREE_excep.ksh new file mode 100755 index 0000000..e2d054f --- /dev/null +++ b/chapter17/fs_mon_LINUX_MBFREE_excep.ksh @@ -0,0 +1,129 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_LINUX_MBFREE_excep.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 2.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: Linux +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# Randy Michael - 08-27-2007 +# Added code to allow you to override the set script default +# for MIN_MB_FREE of FS Space +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +EXCEPTIONS="/usr/local/bin/exceptions" # Override data file +DATA_EXCEPTIONS="/tmp/dfdata.out" # Exceptions file w/o # rows +THISHOST=`hostname` # Hostname of this machine +MIN_MB_FREE="50MB" # Min. MB of Free FS Space + +####### DEFINE FUNCTIONS HERE ######## + +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME FSLIMIT +do + # Do an NFS sanity check + echo $FSNAME | grep ":" >/dev/null \ + && FSNAME=$(echo $FSNAME | cut -d ":" -f2) + if [[ ! -z "$FSLIMIT" && "$FSLIMIT" != '' ]] + then + (( FSLIMIT = $(echo $FSLIMIT | sed s/MB//g) * 1024 )) + if [[ $FSNAME = $FSMOUNT ]] + then + # Get rid of the "MB" if it exists + FSLIMIT=$(echo $FSLIMIT | sed s/MB//g) + if (( $FSMB_FREE < $FSLIMIT )) + then + return 1 # Found out of limit + else + return 2 # Found OK + fi + fi + fi +done < $DATA_EXCEPTIONS # Feed the loop from the bottom!!! + +return 3 # Not found in $EXCEPTIONS file +} + +######## START OF MAIN ############# + +if [[ -s $EXCEPTIONS ]] +then + # Ignore all line beginning with a pound sign, #. + cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS +fi + +# Get the data of interest by stripping out the +# /cdrom row and keeping columns 1, 4 and 6 + +df -k | tail +2 | egrep -v '/cdrom' \ + | awk '{print $1, $4, $6}' > $WORKFILE + +# Format Variables for the proper MB value +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSMB_FREE FSMOUNT +do + if [[ -s $EXCEPTIONS ]] + then + check_exceptions + RC="$?" + if [ $RC -eq 1 ] # Found out of exceptions limit + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + elif [ $RC -eq 2 ] # Found in exceptions to be OK + then # Just a sanity check - We really do nothing here... + # The colon, :, is a NO-OP operator in KSH + + : # No-Op - Do Nothing! + + elif [ $RC -eq 3 ] # Not found in the exceptions file + then + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + fi + else # No Exceptions file use the script default + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + fi +done < $WORKFILE + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_LINUX_PC_MBFREE.ksh b/chapter17/fs_mon_LINUX_PC_MBFREE.ksh new file mode 100755 index 0000000..965d0dd --- /dev/null +++ b/chapter17/fs_mon_LINUX_PC_MBFREE.ksh @@ -0,0 +1,280 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_LINUX_PC_MBFREE.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 4.3.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the MAX_PERCENT value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: Linux +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# Randy Michael - 08-27-2007 +# Added code to allow you to override the set script default +# for MIN_MB_FREE of FS Space +# +# Randy Michael - 08-28-2007 +# Changed the code to handle both %Used and MB of Free Space. +# It does an "auto-detection" but has override capability +# of both the trigger level and the monitoring method using +# the exceptions file pointed to by the $EXCEPTIONS variable +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +EXCEPTIONS="/usr/local/bin/exceptions" # Override data file +DATA_EXCEPTIONS="/tmp/dfdata.out" # Exceptions file w/o # rows +EXCEPT_FILE="N" # Assume no $EXCEPTIONS FILE +THISHOST=`hostname` # Hostname of this machine + +MIN_MB_FREE="100MB" # Min. MB of Free FS Space +MAX_PERCENT="85%" # Max. FS percentage value +FSTRIGGER="1000MB" # Trigger to switch from % Used to MB Free + + +###### FORMAT VARIABLES HERE ###### + +# Both of these variables need to multiplied by 1024 blocks +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) +(( FSTRIGGER = $(echo $FSTRIGGER | sed s/MB//g) * 1024 )) + +####### DEFINE FUNCTIONS HERE ######## + +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME FSLIMIT +do + IN_FILE="N" + + # Do an NFS sanity check and get rid of any ":". + # If this is found it is actaully an error entry + # but we will try to resolve it. It will only + # work if it is an NFS cross mount to the same + # mount point on both machines. + echo $FSNAME | grep ':' >/dev/null \ + && FSNAME=$(echo $FSNAME | cut -d ':' -f2) + + # Check for empty and null variable + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + if [[ $FSNAME = $FSMOUNT ]] # Found it! + then + # Check for "MB" Characters...Set IN_FILE=MB + echo $FSLIMIT | grep MB >/dev/null && IN_FILE="MB" \ + && (( FSLIMIT = $(echo $FSLIMIT \ + | sed s/MB//g) * 1024 )) + # check for "%" Character...Set IN_FILE=PC, for % + echo $FSLIMIT | grep "%" >/dev/null && IN_FILE="PC" \ + && FSLIMIT=$(echo $FSLIMIT | sed s/\%//g) + + case $IN_FILE in + MB) + # Up-case the characters, if they exist + FSLIMIT=$(echo $FSLIMIT | tr '[a-z]' '[A-Z]') + # Get rid of the "MB" if it exists + FSLIMIT=$(echo $FSLIMIT | sed s/MB//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid filesystem "MB" limit + if (( $FSLIMIT >= 0 && $FSLIMIT < $FSSIZE )) + then + if (( $FSMB_FREE < $FSLIMIT )) + then + return 1 # Found out of limit using MB Free method + else + return 3 # Found OK + fi + else + echo "\nERROR: Invalid filesystem MAX for $FSMOUNT - $FSLIMIT" + echo " Exceptions file value must be less than or" + echo " equal to the size of the filesystem measured" + echo " in 1024 bytes\n" + fi + else + echo "\nERROR: Null value specified in excepeptions file" + echo " for the $FSMOUNT mount point.\n" + fi + ;; + PC) + # Strip out the % sign if it exists + PC_USED=$(echo $PC_USED | sed s/\%//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid percentage, i.e. 0-100 + if (( $FSLIMIT >= 0 && $FSLIMIT <= 100 )) + then + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + else + echo "\nERROR: Invalid percentage for $FSMOUNT - $FSLIMIT" + echo " Exceptions file values must be" + echo " between 0 and 100%\n" + fi + else + echo "\nERROR: Null value specified in excepeptions file" + echo " for the $FSMOUNT mount point.\n" + fi + ;; + N) + # Method Not Specified - Use Script Defaults + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + return 1 # Found out of limit using MB Free method + else + return 3 # Found OK + fi + else # This is a standard filesystem + PC_USED=$(echo $PC_USED | sed s/\%//g) # Remove the % + FSLIMIT=$(echo $FSLIMIT | sed s/\%//g) # Remove the % + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + fi + ;; + esac + fi + fi +done < $DATA_EXCEPTIONS # Feed the loop from the bottom!!! + +return 4 # Not found in $EXCEPTIONS file +} + +#################################### + +function display_output +{ +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi +} + +#################################### + +function load_EXCEPTIONS_data +{ +# Ingore any line that begins with a pound sign, # + +cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS +} + +#################################### + +function load_FS_data +{ + df -k | tail +2 | egrep -v '/cdrom' \ + | awk '{print $1, $2, $4, $5, $6}' > $WORKFILE +} + +#################################### +######### START OF MAIN ############ +#################################### + +load_FS_data + +# Do we have a non-zero size $EXCEPTIONS file? + +if [[ -s $EXCEPTIONS ]] +then # Found a non-empty $EXCEPTIONS file + + load_EXCEPTIONS_data + EXCEP_FILE="Y" +fi + +while read FSDEVICE FSSIZE FSMB_FREE PC_USED FSMOUNT +do + if [[ $EXCEP_FILE = "Y" ]] + then + check_exceptions + CE_RC="$?" # Check Exceptions Return Code (CE_RC) + + case $CE_RC in + 1) # Found exceeded in exceptions file by MB Method + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + ;; + 2) # Found exceeded in exceptions file by %Used method + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + ;; + 3) # Found OK in exceptions file + : # NO-OP Do Nothing + ;; + + 4) # Not found in exceptions file - Use Default Triggers + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + else # This is a standard filesystem + PC_USED=$(echo $PC_USED | sed s/\%//g) + MAX_PERCENT=$(echo $MAX_PERCENT | sed s/\%//g) + if (( $PC_USED > $MAX_PERCENT )) + then + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + ;; + esac + + + else # NO $EXECPTIONS FILE USE DEFAULT TRIGGER VALUES + + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem - Use MB Free Method + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + else # This is a standard filesystem - Use % Used Method + PC_USED=$(echo $PC_USED | sed s/\%//g) + MAX_PERCENT=$(echo $MAX_PERCENT | sed s/\%//g) + if (( $PC_USED > $MAX_PERCENT )) + then + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + fi +done < $WORKFILE + +display_output + +# End of Script diff --git a/chapter17/fs_mon_LINUX_excep.ksh b/chapter17/fs_mon_LINUX_excep.ksh new file mode 100755 index 0000000..55d91c2 --- /dev/null +++ b/chapter17/fs_mon_LINUX_excep.ksh @@ -0,0 +1,113 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_LINUX_excep.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 2.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: Linux +# +# REV LIST: +# 08-23-2007 - Randy Michael +# Added code to override the default FSMAX script threshold +# using an "exceptions" file, defined by the $EXCEPTIONS +# variable, that list /mount_point and NEW_MAX% +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +BINDIR="/usr/local/bin" # Local bin directory +THISHOST=`hostname` # Hostname of this machine +FSMAX="85" # Max. FS percentage value +EXCEPTIONS="${BINDIR}/exceptions" # Overrides $FSMAX + +####### DEFINE FUNCTIONS HERE ##### + +function check_exceptions +{ + +# set -x # Uncomment to debug this function + +# Define a data file + +DATA_EXCEPTIONS="/tmp/dfdata.out" + +# Ingore any line that begins with a pound sign, # + +cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS + +while read FSNAME NEW_MAX # Feeding Ddata from Bottom of Loop!!! +do + if [[ $FSNAME = $FSMOUNT ]] # Correct /mount_point? + then # Get rid of the % sign, if it exists! + NEW_MAX=$(echo $NEW_MAX | sed s/\%//g) + + if [ $FSVALUE -gt $NEW_MAX ] + then # Over Limit...Return a "0", zero + return 0 # FOUND MAX OUT - Return 0 + fi + fi + +done < $DATA_EXCEPTIONS # Feed from the bottom of the loop!! + +return 1 # Not found in File +} + +######## START OF MAIN ############# + +# Get the data of interest by stripping out the +# /cdrom row and keeping columns 1, 5 and 6 + +df -k | tail +2 | egrep -v '/cdrom' \ + | awk '{print $1, $5, $6}' > $WORKFILE + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSVALUE FSMOUNT +do + # Strip out the % sign if it exists + FSVALUE=$(echo $FSVALUE | sed s/\%//g) # Remove the % sign + if [[ -s $EXCEPTIONS ]] # Do we have a non-empty file? + then # Found it! + + # Look for the current $FSMOUNT value in the file + # using the check_exceptions function defined above. + + check_exceptions + if [ $? -eq 0 ] # Found it Exceeded!! + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + else # Not exceeded in the file use the script default + if [ $FSVALUE -gt $FSMAX ] # Use Script Default + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi + fi + else # No exceptions file use the script default + if [ $FSVALUE -gt $FSMAX ] # Use Script Default + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi + fi +done < $WORKFILE # Feed the while loop from the bottom... + +# Display output if anything is exceeded... + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_MBFREE.ksh b/chapter17/fs_mon_MBFREE.ksh new file mode 100755 index 0000000..4456e69 --- /dev/null +++ b/chapter17/fs_mon_MBFREE.ksh @@ -0,0 +1,57 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 1.5.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +THISHOST=`hostname` # Hostname of this machine +MIN_MB_FREE="50MB" # Min. MB of Free FS Space + +######## START OF MAIN ############# + +# Get the data of interest by stripping out /dev/cd#, +# /proc rows and keeping columns 1, 4 and 7 + +df -k | tail +2 | egrep -v '/dev/cd[0-9] | /proc' \ + | awk '{print $1, $3, $7}' > $WORKFILE + +# Format Variables +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSMB_FREE FSMOUNT +do + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the % sign + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi +done < $WORKFILE + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_MBFREE_excep.ksh b/chapter17/fs_mon_MBFREE_excep.ksh new file mode 100755 index 0000000..0ae9fe6 --- /dev/null +++ b/chapter17/fs_mon_MBFREE_excep.ksh @@ -0,0 +1,127 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 2.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# Randy Michael - 08-27-2007 +# Added code to allow you to override the set script default +# for MIN_MB_FREE of FS Space +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +EXCEPTIONS="/usr/local/bin/exceptions" # Override data file +DATA_EXCEPTIONS="/tmp/dfdata.out" # Exceptions file w/o # rows +THISHOST=`hostname` # Hostname of this machine +MIN_MB_FREE="50MB" # Min. MB of Free FS Space + +####### DEFINE FUNCTIONS HERE ######## + +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME FSLIMIT +do + # Do an NFS sanity check + echo $FSNAME | grep ":" >/dev/null \ + && FSNAME=$(echo $FSNAME | cut -d ":" -f2) + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + (( FSLIMIT = $(echo $FSLIMIT | sed s/MB//g) * 1024 )) + if [[ $FSNAME = $FSMOUNT ]] + then + # Get rid of the "MB" if it exists + FSLIMIT=$(echo $FSLIMIT | sed s/MB//g) + if (( $FSMB_FREE < $FSLIMIT )) + then + return 1 # Found out of limit + else + return 2 # Found OK + fi + fi + fi +done < $DATA_EXCEPTIONS # Feed the loop from the bottom!!! + +return 3 # Not found in $EXCEPTIONS file +} + +######## START OF MAIN ############# + +if [[ -s $EXCEPTIONS ]] +then + # Ignore all line beginning with a pound sign, #. + cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS +fi + +# Get the data of interest by stripping out /dev/cd#, +# /proc rows and keeping columns 1, 4 and 7 + +df -k | tail +2 | egrep -v '/dev/cd[0-9] | /proc' \ + | awk '{print $1, $3, $7}' > $WORKFILE + +# Format Variables for the proper MB value +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSMB_FREE FSMOUNT +do + if [[ -s $EXCEPTIONS ]] + then + check_exceptions + RC="$?" + if [ $RC -eq 1 ] # Found out of exceptions limit + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + elif [ $RC -eq 2 ] # Found in exceptions to be OK + then # Just a sanity check - We really do nothing here... + # The colon, :, is a NO-OP operator in KSH + + : # No-Op - Do Nothing! + + elif [ $RC -eq 3 ] # Not found in the exceptions file + then + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + fi + else # No Exceptions file use the script default + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + fi +done < $WORKFILE + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_SUNOS.ksh b/chapter17/fs_mon_SUNOS.ksh new file mode 100755 index 0000000..6db6355 --- /dev/null +++ b/chapter17/fs_mon_SUNOS.ksh @@ -0,0 +1,52 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_SUNOS.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 1.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: SUN Solaris +# +# REV LIST: +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +THISHOST=`hostname` # Hostname of this machine +FSMAX="85" # Max. FS percentage value + +######## START OF MAIN ############# + +# Get the data of interest by stripping out rows +# to be ignored and keeping columns 1, 5 and 6 + +df -k | tail +2 | egrep -v '/dev/fd|/etc/mnttab|/proc|/cdrom' \ + | awk '{print $1, $5, $6}' > $WORKFILE + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSVALUE FSMOUNT +do + FSVALUE=$(echo $FSVALUE | sed s/\%//g) # Remove the % sign + if [ $FSVALUE -gt $FSMAX ] + then + echo "$FSDEVICE mounted on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi +done < $WORKFILE + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_SUNOS_MBFREE.ksh b/chapter17/fs_mon_SUNOS_MBFREE.ksh new file mode 100755 index 0000000..c0a5320 --- /dev/null +++ b/chapter17/fs_mon_SUNOS_MBFREE.ksh @@ -0,0 +1,59 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_SUNOS_MBFREE.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 1.5.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: SUN Solaris +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +THISHOST=`hostname` # Hostname of this machine +MIN_MB_FREE="50MB" # Min. MB of Free FS Space + +######## START OF MAIN ############# + +# Get the data of interest by stripping out the rows +# to not monitor and keeping columns 1, 4 and 6 + +df -k | tail +2 | egrep -v 'fd|mnttab|proc|cdrom|objfs' \ + | awk '{print $1, $4, $6}' > $WORKFILE + +# Format Variables +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSMB_FREE FSMOUNT +do + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi +done < $WORKFILE + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_SUNOS_MBFREE_excep.ksh b/chapter17/fs_mon_SUNOS_MBFREE_excep.ksh new file mode 100755 index 0000000..1b41cae --- /dev/null +++ b/chapter17/fs_mon_SUNOS_MBFREE_excep.ksh @@ -0,0 +1,129 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_SUNOS_MBFREE_excep.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 2.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the $MIN_MB_FREE value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: SUN Solaris +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# Randy Michael - 08-27-2007 +# Added code to allow you to override the set script default +# for MIN_MB_FREE of FS Space +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +EXCEPTIONS="/usr/local/bin/exceptions" # Override data file +DATA_EXCEPTIONS="/tmp/dfdata.out" # Exceptions file w/o # rows +THISHOST=`hostname` # Hostname of this machine +MIN_MB_FREE="50MB" # Min. MB of Free FS Space + +####### DEFINE FUNCTIONS HERE ######## + +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME FSLIMIT +do + # Do an NFS sanity check + echo $FSNAME | grep ":" >/dev/null \ + && FSNAME=$(echo $FSNAME | cut -d ":" -f2) + if [[ ! -z "$FSLIMIT" && "$FSLIMIT" != '' ]] + then + (( FSLIMIT = $(echo $FSLIMIT | sed s/MB//g) * 1024 )) + if [[ $FSNAME = $FSMOUNT ]] + then + # Get rid of the "MB" if it exists + FSLIMIT=$(echo $FSLIMIT | sed s/MB//g) + if (( $FSMB_FREE < $FSLIMIT )) + then + return 1 # Found out of limit + else + return 2 # Found OK + fi + fi + fi +done < $DATA_EXCEPTIONS # Feed the loop from the bottom!!! + +return 3 # Not found in $EXCEPTIONS file +} + +######## START OF MAIN ############# + +if [[ -s $EXCEPTIONS ]] +then + # Ignore all line beginning with a pound sign, #. + cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS +fi + +# Get the data of interest by stripping out rows that +# are not monitored and keeping columns 1, 4 and 6 + +df -k | tail +2 | egrep -v '/dev/fd|/etc/mnttab|/proc|/cdrom' \ + | awk '{print $1, $4, $6}' > $WORKFILE + +# Format Variables for the proper MB value +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSMB_FREE FSMOUNT +do + if [[ -s $EXCEPTIONS ]] + then + check_exceptions + RC="$?" + if [ $RC -eq 1 ] # Found out of exceptions limit + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + elif [ $RC -eq 2 ] # Found in exceptions to be OK + then # Just a sanity check - We really do nothing here... + # The colon, :, is a NO-OP operator in KSH + + : # No-Op - Do Nothing! + + elif [ $RC -eq 3 ] # Not found in the exceptions file + then + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + fi + else # No Exceptions file use the script default + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + fi +done < $WORKFILE + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_SUNOS_PC_MBFREE.ksh b/chapter17/fs_mon_SUNOS_PC_MBFREE.ksh new file mode 100755 index 0000000..ea3c6d3 --- /dev/null +++ b/chapter17/fs_mon_SUNOS_PC_MBFREE.ksh @@ -0,0 +1,280 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_SUNOS_PC_MBFREE.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 4.3.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the MAX_PERCENT value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: SUN Solaris +# +# REV LIST: +# Randy Michael - 08-27-2007 +# Changed the code to use MB of free space instead of +# the %Used method. +# +# Randy Michael - 08-27-2007 +# Added code to allow you to override the set script default +# for MIN_MB_FREE of FS Space +# +# Randy Michael - 08-28-2007 +# Changed the code to handle both %Used and MB of Free Space. +# It does an "auto-detection" but has override capability +# of both the trigger level and the monitoring method using +# the exceptions file pointed to by the $EXCEPTIONS variable +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +EXCEPTIONS="/usr/local/bin/exceptions" # Override data file +DATA_EXCEPTIONS="/tmp/dfdata.out" # Exceptions file w/o # rows +EXCEPT_FILE="N" # Assume no $EXCEPTIONS FILE +THISHOST=`hostname` # Hostname of this machine + +MIN_MB_FREE="100MB" # Min. MB of Free FS Space +MAX_PERCENT="85%" # Max. FS percentage value +FSTRIGGER="1000MB" # Trigger to switch from % Used to MB Free + + +###### FORMAT VARIABLES HERE ###### + +# Both of these variables need to multiplied by 1024 blocks +(( MIN_MB_FREE = $(echo $MIN_MB_FREE | sed s/MB//g) * 1024 )) +(( FSTRIGGER = $(echo $FSTRIGGER | sed s/MB//g) * 1024 )) + +####### DEFINE FUNCTIONS HERE ######## + +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME FSLIMIT +do + IN_FILE="N" + + # Do an NFS sanity check and get rid of any ":". + # If this is found it is actaully an error entry + # but we will try to resolve it. It will only + # work if it is an NFS cross mount to the same + # mount point on both machines. + echo $FSNAME | grep ':' >/dev/null \ + && FSNAME=$(echo $FSNAME | cut -d ':' -f2) + + # Check for empty and null variable + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + if [[ $FSNAME = $FSMOUNT ]] # Found it! + then + # Check for "MB" Characters...Set IN_FILE=MB + echo $FSLIMIT | grep MB >/dev/null && IN_FILE="MB" \ + && (( FSLIMIT = $(echo $FSLIMIT \ + | sed s/MB//g) * 1024 )) + # check for "%" Character...Set IN_FILE=PC, for % + echo $FSLIMIT | grep "%" >/dev/null && IN_FILE="PC" \ + && FSLIMIT=$(echo $FSLIMIT | sed s/\%//g) + + case $IN_FILE in + MB) + # Up-case the characters, if they exist + FSLIMIT=$(echo $FSLIMIT | tr '[a-z]' '[A-Z]') + # Get rid of the "MB" if it exists + FSLIMIT=$(echo $FSLIMIT | sed s/MB//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid filesystem "MB" limit + if (( $FSLIMIT >= 0 && $FSLIMIT < $FSSIZE )) + then + if (( $FSMB_FREE < $FSLIMIT )) + then + return 1 # Found out of limit using MB Free method + else + return 3 # Found OK + fi + else + echo "\nERROR: Invalid filesystem MAX for $FSMOUNT - $FSLIMIT" + echo " Exceptions file value must be less than or" + echo " equal to the size of the filesystem measured" + echo " in 1024 bytes\n" + fi + else + echo "\nERROR: Null value specified in excepeptions file" + echo " for the $FSMOUNT mount point.\n" + fi + ;; + PC) + # Strip out the % sign if it exists + PC_USED=$(echo $PC_USED | sed s/\%//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid percentage, i.e. 0-100 + if (( $FSLIMIT >= 0 && $FSLIMIT <= 100 )) + then + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + else + echo "\nERROR: Invalid percentage for $FSMOUNT - $FSLIMIT" + echo " Exceptions file values must be" + echo " between 0 and 100%\n" + fi + else + echo "\nERROR: Null value specified in excepeptions file" + echo " for the $FSMOUNT mount point.\n" + fi + ;; + N) + # Method Not Specified - Use Script Defaults + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + return 1 # Found out of limit using MB Free method + else + return 3 # Found OK + fi + else # This is a standard filesystem + PC_USED=$(echo $PC_USED | sed s/\%//g) # Remove the % + FSLIMIT=$(echo $FSLIMIT | sed s/\%//g) # Remove the % + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + fi + ;; + esac + fi + fi +done < $DATA_EXCEPTIONS # Feed the loop from the bottom!!! + +return 4 # Not found in $EXCEPTIONS file +} + +#################################### + +function display_output +{ +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi +} + +#################################### + +function load_EXCEPTIONS_data +{ +# Ingore any line that begins with a pound sign, # + +cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS +} + +#################################### + +function load_FS_data +{ + df -k | tail +2 | egrep -v '/dev/fd|/etc/mnttab|/proc|/cdrom' \ + | awk '{print $1, $2, $4, $5, $6}' > $WORKFILE +} + +#################################### +######### START OF MAIN ############ +#################################### + +load_FS_data + +# Do we have a non-zero size $EXCEPTIONS file? + +if [[ -s $EXCEPTIONS ]] +then # Found a non-empty $EXCEPTIONS file + + load_EXCEPTIONS_data + EXCEP_FILE="Y" +fi + +while read FSDEVICE FSSIZE FSMB_FREE PC_USED FSMOUNT +do + if [[ $EXCEP_FILE = "Y" ]] + then + check_exceptions + CE_RC="$?" # Check Exceptions Return Code (CE_RC) + + case $CE_RC in + 1) # Found exceeded in exceptions file by MB Method + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + ;; + 2) # Found exceeded in exceptions file by %Used method + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + ;; + 3) # Found OK in exceptions file + : # NO-OP Do Nothing + ;; + + 4) # Not found in exceptions file - Use Default Triggers + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + else # This is a standard filesystem + PC_USED=$(echo $PC_USED | sed s/\%//g) + MAX_PERCENT=$(echo $MAX_PERCENT | sed s/\%//g) + if (( $PC_USED > $MAX_PERCENT )) + then + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + ;; + esac + + + else # NO $EXECPTIONS FILE USE DEFAULT TRIGGER VALUES + + if (( $FSSIZE >= $FSTRIGGER )) + then # This is a "large" filesystem - Use MB Free Method + FSMB_FREE=$(echo $FSMB_FREE | sed s/MB//g) # Remove the "MB" + if (( $FSMB_FREE < $MIN_MB_FREE )) + then + (( FS_FREE_OUT = $FSMB_FREE / 1000 )) + echo "$FSDEVICE mounted on $FSMOUNT has ${FS_FREE_OUT}MB Free" \ + >> $OUTFILE + fi + else # This is a standard filesystem - Use % Used Method + PC_USED=$(echo $PC_USED | sed s/\%//g) + MAX_PERCENT=$(echo $MAX_PERCENT | sed s/\%//g) + if (( $PC_USED > $MAX_PERCENT )) + then + echo "$FSDEVICE mount on $FSMOUNT is ${PC_USED}%" \ + >> $OUTFILE + fi + fi + fi +done < $WORKFILE + +display_output + +# End of Script diff --git a/chapter17/fs_mon_SUNOS_excep.ksh b/chapter17/fs_mon_SUNOS_excep.ksh new file mode 100755 index 0000000..885461e --- /dev/null +++ b/chapter17/fs_mon_SUNOS_excep.ksh @@ -0,0 +1,113 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon_SUNOS_excep.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 2.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: SUN Solaris +# +# REV LIST: +# 08-23-2007 - Randy Michael +# Added code to override the default FSMAX script threshold +# using an "exceptions" file, defined by the $EXCEPTIONS +# variable, that list /mount_point and NEW_MAX% +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +BINDIR="/usr/local/bin" # Local bin directory +THISHOST=`hostname` # Hostname of this machine +FSMAX="85" # Max. FS percentage value +EXCEPTIONS="${BINDIR}/exceptions" # Overrides $FSMAX + +####### DEFINE FUNCTIONS HERE ##### + +function check_exceptions +{ + +# set -x # Uncomment to debug this function + +# Define a data file + +DATA_EXCEPTIONS="/tmp/dfdata.out" + +# Ingore any line that begins with a pound sign, # + +cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS + +while read FSNAME NEW_MAX # Feeding Ddata from Bottom of Loop!!! +do + if [[ $FSNAME = $FSMOUNT ]] # Correct /mount_point? + then # Get rid of the % sign, if it exists! + NEW_MAX=$(echo $NEW_MAX | sed s/\%//g) + + if [ $FSVALUE -gt $NEW_MAX ] + then # Over Limit...Return a "0", zero + return 0 # FOUND MAX OUT - Return 0 + fi + fi + +done < $DATA_EXCEPTIONS # Feed from the bottom of the loop!! + +return 1 # Not found in File +} + +######## START OF MAIN ############# + +# Get the data of interest by stripping out the rows that +# are not monitored and keeping columns 1, 4 and 7 + +df -k | tail +2 | egrep -v '/dev/fd|/etc/mnttab|/proc|/cdrom' \ + | awk '{print $1, $5, $6}' > $WORKFILE + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSVALUE FSMOUNT +do + # Strip out the % sign if it exists + FSVALUE=$(echo $FSVALUE | sed s/\%//g) # Remove the % sign + if [[ -s $EXCEPTIONS ]] # Do we have a non-empty file? + then # Found it! + + # Look for the current $FSMOUNT value in the file + # using the check_exceptions function defined above. + + check_exceptions + if [ $? -eq 0 ] # Found it Exceeded!! + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + else # Not exceeded in the file use the script default + if [ $FSVALUE -gt $FSMAX ] # Use Script Default + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi + fi + else # No exceptions file use the script default + if [ $FSVALUE -gt $FSMAX ] # Use Script Default + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi + fi +done < $WORKFILE # Feed the while loop from the bottom... + +# Display output if anything is exceeded... + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_excep.ksh b/chapter17/fs_mon_excep.ksh new file mode 100755 index 0000000..9e98f57 --- /dev/null +++ b/chapter17/fs_mon_excep.ksh @@ -0,0 +1,113 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 2.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: AIX, at least +# +# REV LIST: +# 08-23-2007 - Randy Michael +# Added code to override the default FSMAX script threshold +# using an "exceptions" file, defined by the $EXCEPTIONS +# variable, that list /mount_point and NEW_MAX% +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty +BINDIR="/usr/local/bin" # Local bin directory +THISHOST=`hostname` # Hostname of this machine +FSMAX="85" # Max. FS percentage value +EXCEPTIONS="${BINDIR}/exceptions" # Overrides $FSMAX + +####### DEFINE FUNCTIONS HERE ##### + +function check_exceptions +{ + +# set -x # Uncomment to debug this function + +# Define a data file + +DATA_EXCEPTIONS="/tmp/dfdata.out" + +# Ingore any line that begins with a pound sign, # + +cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS + +while read FSNAME NEW_MAX # Feeding Data from Bottom of Loop!!! +do + if [[ $FSNAME = $FSMOUNT ]] # Correct /mount_point? + then # Get rid of the % sign, if it exists! + NEW_MAX=$(echo $NEW_MAX | sed s/\%//g) + + if [ $FSVALUE -gt $NEW_MAX ] + then # Over Limit...Return a "0", zero + return 0 # FOUND MAX OUT - Return 0 + fi + fi + +done < $DATA_EXCEPTIONS # Feed from the bottom of the loop!! + +return 1 # Not found in File +} + +######## START OF MAIN ############# + +# Get the data of interest by stripping out /dev/cd#, +# /proc rows and keeping columns 1, 4 and 7 + +df -k | tail +2 | egrep -v '/dev/cd[0-9] | /proc' \ + | awk '{print $1, $4, $7}' > $WORKFILE + +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSVALUE FSMOUNT +do + # Strip out the % sign if it exists + FSVALUE=$(echo $FSVALUE | sed s/\%//g) # Remove the % sign + if [[ -s $EXCEPTIONS ]] # Do we have a non-empty file? + then # Found it! + + # Look for the current $FSMOUNT value in the file + # using the check_exceptions function defined above. + + check_exceptions + if [ $? -eq 0 ] # Found it Exceeded!! + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + else # Not exceeded in the file use the script default + if [ $FSVALUE -gt $FSMAX ] # Use Script Default + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi + fi + else # No exceptions file use the script default + if [ $FSVALUE -gt $FSMAX ] # Use Script Default + then + echo "$FSDEVICE mount on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi + fi +done < $WORKFILE # Feed the while loop from the bottom... + +# Display output if anything is exceeded... + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/fs_mon_except_MB.ksh b/chapter17/fs_mon_except_MB.ksh new file mode 100755 index 0000000..305908c --- /dev/null +++ b/chapter17/fs_mon_except_MB.ksh @@ -0,0 +1,182 @@ +#!/usr/bin/ksh +# +# SCRIPT: fs_mon.ksh +# AUTHOR: Randy Michael +# DATE: 08-22-2007 +# REV: 3.1.P +# PURPOSE: This script is used to monitor for full filesystems, +# which is defined as "exceeding" the FSMAX value. +# A message is displayed for all "full" filesystems. +# +# PLATFORM: AIX, at least +# +# REV LIST: +# 08-23-2007 - Randy Michael +# Added code to override the default FSMAX script threshold +# using an "exceptions" file, defined by the $EXCEPTIONS +# variable, that list /mount_point and NEW_MAX% +# +# 08-26-2007 - Randy Michael +# Added code to check both %USED and MB of Free space +# with auto detection to switch from %USED to MB Free +# for filesystems of 3GB and greater. +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this script +# +##### DEFINE FILES AND VARIABLES HERE #### + +typeset -i FSTOTMB + +WORKFILE="/tmp/df.work" # Holds filesystem data +>$WORKFILE # Initialize to empty file +OUTFILE="/tmp/df.outfile" # Output display file +>$OUTFILE # Initialize to empty file +BINDIR="/usr/local/bin" # Local bin directory +THISHOST=`hostname` # Hostname of this machine +FSMAX="85%" # Max. FS percentage value +EXCEPTIONS="${BINDIR}/exceptions" # Overrides $FSMAX + +MBSWITCH="1000MB" # Number of MB to switch from %USED to MB Free +MBTRIGGER="40MB" # Minimum MB free before trigger + + +####### DEFINE FUNCTIONS HERE ##### + +function check_exceptions +{ + +# set -x # Uncomment to debug this function + +# Define a data file + +DATA_EXCEPTIONS="/tmp/dfdata.out" + +# Ingore any line that begins with a pound sign, # + +cat $EXCEPTIONS | grep -v "^#" > $DATA_EXCEPTIONS + +while read FSNAME NEW_MAX # Feeding Ddata from Bottom of Loop!!! +do + if [[ "$FSNAME" = "$FSMOUNT" ]] # Correct /mount_point? + then + if (( $FSTOTMB >= $MBSWITCH )) + then + # MB Free Space - Get rid of "MB" if exists! + NEW_MIN_MB=$(echo $NEW_MAX | sed s/MB//g) + (( NEW_MIN_MB = $NEW_MIN_MB * 1024000 )) # In bytes + + if (( $FSFREEMB < $NEW_MIN_MB )) + then + return 2 # Found below MB Trigger Level + else + return 3 # Found but OK + fi + else + # Get rid of the % sign, if it exists! + NEW_MAX=$(echo $NEW_MAX | sed s/\%//g) + + if [ $FSVALUE -gt $NEW_MAX ] + then # Over Limit...Return a "0", zero + return 0 # FOUND MAX OUT - Return 0 + fi + fi + fi + +done < $DATA_EXCEPTIONS # Feed from the bottom of the loop!! + +return 1 # Not found in File +} + +######## START OF MAIN ############# + +####### FORMAT VARIABLES ########### + +# Get rib of the "MB" letters if they exist +MBTRIGGER=$(echo $MBTRIGGER | sed s/MB//g ) +MBSWITCH=$(echo $MBSWITCH | sed s/MB//g ) + +# Get rid of the "%" if it exists +FSMAX=$(echo $FSMAX | sed s/\%//g ) + +# Get an "actual" value in Mega Bytes +(( MBSWITCH = $MBSWITCH * 1024000 )) +(( MBTRIGGER = $MBTRIGGER * 1024000 )) + +#################################### +# Get the data of interest by stripping out /dev/cd#, +# /proc rows and keeping columns 1, 4 and 7 + +df -k | tail +2 | egrep -v '/dev/cd[0-9] | /proc' \ + | awk '{print $1, $2, $3, $4, $7}' > $WORKFILE + +#################################### +# Loop through each line of the file and compare column 2 + +while read FSDEVICE FSTOTKB FSFREEKB FSVALUE FSMOUNT +do + # Strip out the % sign if it exists + FSVALUE=$(echo $FSVALUE | sed s/\%//g) # Remove the % sign + (( FSTOTMB = $FSTOTKB * 1000 )) + (( FSFREEMB = $FSFREEKB * 1000 )) + if [[ -s $EXCEPTIONS ]] # Do we have a non-empty file? + then # Found it! + + # Look for the current $FSMOUNT value in the file + # using the check_exceptions function defined above. + + check_exceptions + RC="$?" + if [ $RC -eq 0 ] # Found it Exceeded by Percentage + then + echo "$FSDEVICE mounted on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + elif [ $RC -eq 2 ] # Found it Exceeded by MB Free + then + (( FSFREEMB = $FSFREEMB / 1000000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FSFREEMB}MB Free" \ + >> $OUTFILE + elif [ $RC -ne 3 ] + then + if (( $FSTOTMB >= $MBSWITCH )) + then + if (( $FSFREEMB < $MBTRIGGER )) # Use MB of Free Space? + then + (( FSFREEMB = $FSFREEMB / 1000000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FSFREEMB}MB Free" \ + >> $OUTFILE + elif [ $FSVALUE -gt $FSMAX ] # Use Script Default of % Used + then + echo "$FSDEVICE mounted on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + fi + fi + fi + else # No exceptions file use the script default + if (( $FSTOTMB >= $MBSWITCH )) + then + if (( $FSFREEMB < $MBTRIGGER )) + then + (( FSFREEMB = $FSFREEMB / 1000000 )) + echo "$FSDEVICE mounted on $FSMOUNT only has ${FSFREEMB}MB Free" \ + >> $OUTFILE + fi + else + if [ $FSVALUE -gt $FSMAX ] # Use Script Default + then + echo "$FSDEVICE mounted on $FSMOUNT is ${FSVALUE}%" \ + >> $OUTFILE + + fi + fi + fi +done < $WORKFILE # Feed the while loop from the bottom... + +# Display output if anything is exceeded... + +if [[ -s $OUTFILE ]] +then + echo "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi diff --git a/chapter17/function_check_exceptions b/chapter17/function_check_exceptions new file mode 100755 index 0000000..4efdaae --- /dev/null +++ b/chapter17/function_check_exceptions @@ -0,0 +1,119 @@ +function check_exceptions +{ +# set -x # Uncomment to debug this function + +while read FSNAME FSLIMIT +do + IN_FILE="N" + + # Do an NFS sanity check and get rid of any .:.. + # If this is found it is actually an error entry + # but we will try to resolve it. It will only + # work if it is an NFS cross mount to the same + # mount point on both machines. + $ECHO $FSNAME | grep ':' >/dev/null \ + && FSNAME=$($ECHO $FSNAME | cut -d ':' -f2) + + # Check for empty and null variable + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + if [[ $FSNAME = $FSMOUNT ]] # Found it! + then + # Check for "MB" Characters...Set IN_FILE=MB + $ECHO $FSLIMIT | grep MB >/dev/null && IN_FILE="MB" \ + && (( FSLIMIT = $($ECHO $FSLIMIT \ + | sed s/MB//g) * 1024 )) + # check for '%' Character...Set IN_FILE=PC, for % + $ECHO $FSLIMIT | grep '%' >/dev/null && IN_FILE="PC" \ + && FSLIMIT=$($ECHO $FSLIMIT | sed s/\%//g) + + case $IN_FILE in + MB) # Use MB of Free Space Method + # Up-case the characters, if they exist + FSLIMIT=$($ECHO $FSLIMIT | tr '[a-z]' '[A-Z]') + # Get rid of the 'MB' if it exists + FSLIMIT=$($ECHO $FSLIMIT | sed s/MB//g) + # Test for blank and null values + if [[ ! -z $FSLIMIT && $FSLIMIT != '' ]] + then + # Test for a valid filesystem 'MB' limit + if (( FSLIMIT >= 0 && FSLIMIT < FSSIZE )) + then + if (( FSMB_FREE < FSLIMIT )) + then + return 1 # Found out of limit + # using MB Free method + else + return 3 # Found OK + fi + else + $ECHO "\nERROR: Invalid filesystem MAX for\ + $FSMOUNT - $FSLIMIT" + $ECHO " Exceptions file value must be less\ + than or" + $ECHO " equal to the size of the filesystem\ + measured" + $ECHO " in 1024 bytes\n" + fi + else + $ECHO "\nERROR: Null value specified in exceptions\ + file" + $ECHO " for the $FSMOUNT mount point.\n" + fi + ;; + PC) # Use Filesystem %Used Method + # Strip out the % sign if it exists + PC_USED=$($ECHO $PC_USED | sed s/\%//g) + # Test for blank and null values + if [[ ! -z "$FSLIMIT" && "$FSLIMIT" != '' ]] + then + # Test for a valid percentage, i.e. 0-100 + if (( FSLIMIT >= 0 && FSLIMIT <= 100 )) + then + if (( $PC_USED > $FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + else + $ECHO "\nERROR: Invalid percentage for $FSMOUNT -\ + $FSLIMIT" + $ECHO " Exceptions file values must be" + $ECHO " between 0 and 100%\n" + fi + else + $ECHO "\nERROR: Null value specified in exceptions\ + file" + $ECHO " for the $FSMOUNT mount point.\n" + fi + ;; + N) # Method Not Specified - Use Script Defaults + if (( FSSIZE >= FSTRIGGER )) + then # This is a "large" filesystem + if (( FSMB_FREE < MIN_MB_FREE )) + then + return 1 # Found out of limit + # using MB Free method + else + return 3 # Found OK + fi + else # This is a standard filesystem + PC_USED=$($ECHO $PC_USED | sed s/\%//g) # Remove % + FSLIMIT=$($ECHO $FSLIMIT | sed s/\%//g) # Remove % + if (( PC_USED > FSLIMIT )) + then + return 2 # Found exceeded by % Used method + else + return 3 # Found OK + fi + fi + ;; + esac + fi + fi +done < $DATA_EXCEPTIONS # Feed the loop from the bottom!!! + +return 4 # Not found in $EXCEPTIONS file +} + diff --git a/chapter17/function_display_output b/chapter17/function_display_output new file mode 100755 index 0000000..8bba5a0 --- /dev/null +++ b/chapter17/function_display_output @@ -0,0 +1,10 @@ +function display_output +{ +if [[ -s $OUTFILE ]] +then + $ECHO "\nFull Filesystem(s) on $THISHOST\n" + cat $OUTFILE + print +fi +} + diff --git a/chapter17/function_get_OS_info b/chapter17/function_get_OS_info new file mode 100755 index 0000000..abbd543 --- /dev/null +++ b/chapter17/function_get_OS_info @@ -0,0 +1,11 @@ +function get_OS_info +{ +# For a few commands it is necessary to know the OS and its level +# to execute the proper command syntax. This will always return +# the OS in UPPERCASE + +typeset -u OS # Use the UPPERCASE values for the OS variable +OS=`uname` # Grab the Operating system, i.e. AIX, HP-UX +print $OS # Send back the UPPERCASE value +} + diff --git a/chapter17/function_load_AIX_FS_data b/chapter17/function_load_AIX_FS_data new file mode 100755 index 0000000..e8c16aa --- /dev/null +++ b/chapter17/function_load_AIX_FS_data @@ -0,0 +1,7 @@ +function load_AIX_FS_data +{ + + df -k | tail +2 | egrep -v "/dev/cd[0-9]|/proc" \ + | awk '{print $1, $2, $3, $4, $7}' > $WORKFILE +} + diff --git a/chapter17/function_load_EXCEPTIONS_data b/chapter17/function_load_EXCEPTIONS_data new file mode 100755 index 0000000..b3ed9cf --- /dev/null +++ b/chapter17/function_load_EXCEPTIONS_data @@ -0,0 +1,8 @@ +function load_EXCEPTIONS_data +{ +# Ingore any line that begins with a pound sign, # +# and omit all blank lines + +cat $EXCEPTIONS | grep -v "^#" | sed /^$/d > $DATA_EXCEPTIONS +} + diff --git a/chapter17/function_load_FS_data b/chapter17/function_load_FS_data new file mode 100755 index 0000000..8510094 --- /dev/null +++ b/chapter17/function_load_FS_data @@ -0,0 +1,6 @@ +function load_FS_data +{ + df -k | tail +2 | egrep -v '/dev/cd[0-9] | /proc' \ + | awk '{print $1, $2, $3, $4, $7}' > $WORKFILE +} + diff --git a/chapter17/function_load_HP_UX_FS_data b/chapter17/function_load_HP_UX_FS_data new file mode 100755 index 0000000..a094f22 --- /dev/null +++ b/chapter17/function_load_HP_UX_FS_data @@ -0,0 +1,7 @@ +function load_HP_UX_FS_data +{ + + bdf | tail +2 | egrep -v "/cdrom" \ + | awk '{print $1, $2, $4, $5, $6}' > $WORKFILE +} + diff --git a/chapter17/function_load_LINUX_FS_data b/chapter17/function_load_LINUX_FS_data new file mode 100755 index 0000000..38e4e89 --- /dev/null +++ b/chapter17/function_load_LINUX_FS_data @@ -0,0 +1,7 @@ +function load_LINUX_FS_data +{ + + df -k | tail -n +2 | egrep -v "/cdrom"\ + | awk '{print $1, $2, $4, $5, $6}' > $WORKFILE +} + diff --git a/chapter17/function_load_OpenBSD_FS_data b/chapter17/function_load_OpenBSD_FS_data new file mode 100755 index 0000000..4f1e809 --- /dev/null +++ b/chapter17/function_load_OpenBSD_FS_data @@ -0,0 +1,6 @@ +function load_OpenBSD_FS_data +{ + df -k | tail +2 | egrep -v "/mnt/cdrom"\ + | awk '{print $1, $2, $4, $5, $6}' > $WORKFILE +} + diff --git a/chapter17/function_load_Solaris_FS_data b/chapter17/function_load_Solaris_FS_data new file mode 100755 index 0000000..a1ca4a0 --- /dev/null +++ b/chapter17/function_load_Solaris_FS_data @@ -0,0 +1,7 @@ +function load_Solaris_FS_data +{ + + df -k | tail +2 | egrep -v "/dev/fd|/etc/mnttab|/proc"\ + | awk '{print $1, $2, $4, $5, $6}' > $WORKFILE +} + diff --git a/chapter18/AIX_paging_mon.ksh b/chapter18/AIX_paging_mon.ksh new file mode 100755 index 0000000..3e9d80e --- /dev/null +++ b/chapter18/AIX_paging_mon.ksh @@ -0,0 +1,86 @@ +#!/usr/bin/ksh +# +# SCRIPT: AIX_paging_mon.ksh +# +# AUTHOR: Randy Michael +# DATE: 5/31/2007 +# REV: 1.1.P +# +# PLATFORM: AIX Only +# +# PURPOSE: This shell script is used to produce a report of +# the system's paging space statistics including: +# +# Total paging space in MB, MB of Free paging space, +# MB of Used paging space, % of paging space Used, and +# % of paging space Free +# +# REV LIST: +# +# +# set -x # Uncomment to debug this shell script +# set -n # Uncomment to check command syntax without any execution +# +########################################################### +################ DEFINE VARIABLES HERE #################### + +PC_LIMIT=65 # Percentage Upper limit of paging space + # before notification + +THISHOST=$(hostname) # Host name of this machine +PAGING_STAT=/tmp/paging_stat.out # Paging Stat hold file + +########################################################### +################ INITIALIZE THE REPORT #################### + +echo "\nPaging Space Report for $THISHOST\n" +date + +########################################################### +############# CAPTURE AND PROCESS THE DATA ################ + +# Load the data in a file without the column headings + +lsps -s | tail +2 > $PAGING_STAT + +# Start a while loop and feed the loop from the bottom using +# the $PAGING_STAT file as redirected input + +while read TOTAL PERCENT +do + # Clean up the data by removing the suffixes + PAGING_MB=$(echo $TOTAL | cut -d 'MB' -f1) + PAGING_PC=$(echo $PERCENT | cut -d% -f1) + + # Calculate the missing data: %Free, MB used and MB free + (( PAGING_PC_FREE = 100 - PAGING_PC )) + (( MB_USED = PAGING_MB * PAGING_PC / 100 )) + (( MB_FREE = PAGING_MB - MB_USED )) + + # Produce the rest of the paging space report: + echo "\nTotal MB of Paging Space:\t$TOTAL" + echo "Total MB of Paging Space Used:\t${MB_USED}MB" + echo "Total MB of Paging Space Free:\t${MB_FREE}MB" + echo "\nPercent of Paging Space Used:\t${PERCENT}" + echo "\nPercent of Paging Space Free:\t${PAGING_PC_FREE}%" + + # Check for paging space exceeded the predefined limit + if ((PC_LIMIT <= PAGING_PC)) + then + # Paging space is over the limit, send notification + + tput smso # Turn on reverse video! + + echo "\n\nWARNING: Paging Space has Exceeded the ${PC_LIMIT}% \ +Upper Limit!\n" + + tput rmso # Turn off reverse video + fi + +done < $PAGING_STAT + +rm -f $PAGING_STAT + +# Add an extra new line to the output + +echo "\n" diff --git a/chapter18/HP-UX_swap_mon.ksh b/chapter18/HP-UX_swap_mon.ksh new file mode 100755 index 0000000..9887829 --- /dev/null +++ b/chapter18/HP-UX_swap_mon.ksh @@ -0,0 +1,71 @@ +#!/usr/bin/ksh +# +# SCRIPT: HP-UX_swap_mon.ksh +# +# AUTHOR: Randy Michael +# DATE: 5/31/2007 +# REV: 1.1.P +# +# PLATFORM: HP-UX Only +# +# PURPOSE: This shell script is used to produce a report of +# the system's paging space statistics including: +# +# Total paging space in MB, MB of Free paging space, +# MB of Used paging space, % of paging space Used, and +# % of paging space Free +# +# REV LIST: +# +# +# set -x # Uncomment to debug this shell script +# set -n # Uncomment to check command syntax without any execution +# +################ DEFINE VARIABLES HERE #################### + +PC_LIMIT=65 # Percentage Upper limit of paging space + # before notification + +THISHOST=$(hostname) # Host name of this machine + +########################################################### +################ INITIALIZE THE REPORT #################### + +echo "\nSwap Space Report for $THISHOST\n" +date + +########################################################### +############# CAPTURE AND PROCESS THE DATA ################ + +# Start a while read loop by using the piped in input from +# the swapinfo -tm command output. + + +swapinfo -tm | grep dev | while read junk SW_TOTAL SW_USED \ + SW_FREE PERCENT_USED junk2 +do + # Calculate the percentage of free swap space + + ((PERCENT_FREE = 100 - $(echo $PERCENT_USED | cut -d% -f1) )) + + echo "\nTotal Amount of Swap Space:\t${SW_TOTAL}MB" + echo "Total MB of Swap Space Used:\t${SW_USED}MB" + echo "Total MB of Swap Space Free:\t${SW_FREE}MB" + echo "\nPercent of Swap Space Used:\t${PERCENT_USED}" + echo "\nPercent of Swap Space Free:\t${PERCENT_FREE}%" + + # Check for paging space exceeded the predefined limit + + if (( PC_LIMIT <= $(echo $PERCENT_USED | cut -d% -f1) )) + then + # Swap space is over the predefined limit, send notification + + tput smso # Turn on reverse video! + echo "\n\nWARNING: Swap Space has Exceeded the\ + ${PC_LIMIT}% Upper Limit!\n" + tput rmso # Turn reverse video off! + fi + +done + +echo "\n" diff --git a/chapter18/HP_UX_swap_mon b/chapter18/HP_UX_swap_mon new file mode 100755 index 0000000..e431463 --- /dev/null +++ b/chapter18/HP_UX_swap_mon @@ -0,0 +1,37 @@ +function HP_UX_swap_mon +{ + +# Start a while read loop by using the piped in input from +# the swapinfo -tm command output. + + +swapinfo -tm | grep dev | while read junk SW_TOTAL SW_USED \ + SW_FREE PERCENT_USED junk2 +do + # Calculate the percentage of free swap space + + ((PERCENT_FREE = 100 - $(echo $PERCENT_USED | cut -d% -f1) )) + + echo "\nTotal Amount of Swap Space:\t${SW_TOTAL}MB" + echo "Total MB of Swap Space Used:\t${SW_USED}MB" + echo "Total MB of Swap Space Free:\t${SW_FREE}MB" + echo "\nPercent of Swap Space Used:\t${PERCENT_USED}" + echo "\nPercent of Swap Space Free:\t${PERCENT_FREE}%" + + # Check for paging space exceeded the predefined limit + + if (( PC_LIMIT <= $(echo $PERCENT_USED | cut -d% -f1) )) + then + # Swap space is over the predefined limit, send notification + + tput smso # Turn on reverse video! + echo "\n\nWARNING: Swap Space has Exceeded the\ + ${PC_LIMIT}% Upper Limit!\n" + tput rmso # Turn reverse video off! + fi + +done + +echo "\n" +} + diff --git a/chapter18/Linux_swap_mon b/chapter18/Linux_swap_mon new file mode 100755 index 0000000..fd59421 --- /dev/null +++ b/chapter18/Linux_swap_mon @@ -0,0 +1,46 @@ +function Linux_swap_mon +{ + +free -m | grep -i swap | while read junk SW_TOTAL SW_USED SW_FREE +do + +# Use the bc utility in a here document to calculate +# the percentage of free and used swap space. + +PERCENT_USED=$(bc < $PAGING_STAT + +# Start a while loop and feed the loop from the bottom using +# the $PAGING_STAT file as redirected input + +while read TOTAL PERCENT +do + # Clean up the data by removing the suffixes + PAGING_MB=$(echo $TOTAL | cut -d 'MB' -f1) + PAGING_PC=$(echo $PERCENT | cut -d% -f1) + + # Calculate the missing data: %Free, MB used and MB free + (( PAGING_PC_FREE = 100 - PAGING_PC )) + (( MB_USED = PAGING_MB * PAGING_PC / 100 )) + (( MB_FREE = PAGING_MB - MB_USED )) + + # Produce the rest of the paging space report: + echo "\nTotal MB of Paging Space:\t$TOTAL" + echo "Total MB of Paging Space Used:\t${MB_USED}MB" + echo "Total MB of Paging Space Free:\t${MB_FREE}MB" + echo "\nPercent of Paging Space Used:\t${PERCENT}" + echo "\nPercent of Paging Space Free:\t${PAGING_PC_FREE}%" + + # Check for paging space exceeded the predefined limit + if ((PC_LIMIT <= PAGING_PC)) + then + # Paging space is over the limit, send notification + + tput smso # Turn on reverse video! + + echo "\n\nWARNING: Paging Space has Exceeded the ${PC_LIMIT}% Upper Limit!\n" + + tput rmso # Turn off reverse video + fi + +done < $PAGING_STAT + +rm -f $PAGING_STAT + +# Add an extra new line to the output + +echo "\n" +} + +########################################################### + +function OpenBSD_swap_mon +{ +########################################################### +############# CAPTURE AND PROCESS THE DATA ################ + +# Load the data in a file without the column headings + +swapctl -lk | tail +2 | awk '{print $2, $3, $4, $5}' \ + | while read KB_TOT KB_USED KB_AVAIL PC_USED +do + (( TOTAL = KB_TOT / 1000 )) + (( MB_USED = KB_USED / 1000 )) + (( MB_FREE = KB_AVAIL / 1000 )) + PC_FREE_NO_PC=$(echo $PC_USED | awk -F '%' '{print $1}') + (( PC_FREE = 100 - PC_FREE_NO_PC )) + + # Produce the rest of the paging space report: + echo "\nTotal MB of Paging Space:\t${TOTAL}MB" + echo "Total MB of Paging Space Used:\t${MB_USED}MB" + echo "Total MB of Paging Space Free:\t${MB_FREE}MB" + echo "\nPercent of Paging Space Used:\t${PC_USED}" + echo "\nPercent of Paging Space Free:\t${PC_FREE}%\n" +done + + +# Check for paging space exceeded the predefined limit + +if (( PC_LIMIT <= PC_FREE_NO_PC )) +then + echo "\n\nWARNING: Paging Space has Exceeded the ${PC_LIMIT}% Upper Limit!\n" +fi +} + +########################################################### +################## BEGINNING OF MAIN ###################### +########################################################### + +# Find the Operating System and execute the correct function + +case $(uname) in + + AIX) AIX_paging_mon + ;; + HP-UX) HP_UX_swap_mon + ;; + Linux) Linux_swap_mon + ;; + SunOS) SUN_swap_mon + ;; + OpenBSD) OpenBSD_swap_mon + ;; + *) echo "\nERROR: Unsupported Operating System...EXITING\n" + exit 1 + ;; +esac + +# End of all-in-one_swapmon.ksh diff --git a/chapter18/linux_swap_mon.ksh b/chapter18/linux_swap_mon.ksh new file mode 100755 index 0000000..6bda012 --- /dev/null +++ b/chapter18/linux_swap_mon.ksh @@ -0,0 +1,80 @@ +#!/usr/bin/ksh +# +# SCRIPT: linux_swap_mon.ksh +# +# AUTHOR: Randy Michael +# DATE: 5/31/2007 +# REV: 1.1.P +# +# PLATFORM: Linux Only +# +# +# PURPOSE: This shell script is used to produce a report of +# the system's paging space statistics including: +# +# Total paging space in MB, MB of Free paging space, +# MB of Used paging space, % of paging space Used, and +# % of paging space Free +# +# REV LIST: +# +# +# set -x # Uncomment to debug this shell script +# set -n # Uncomment to check command syntax without any execution +# +########################################################### +################ DEFINE VARIABLES HERE #################### + +THISHOST=$(hostname) # Host name of this machine +PC_LIMIT=65 # Upper limit of Swap space percentage + # before notification + +########################################################### +################ INITIALIZE THE REPORT #################### + +echo "\nSwap Space Report for $THISHOST\n" +date + +########################################################### +############# CAPTURE AND PROCESS THE DATA ################ + +free -m | grep -i swap | while read junk SW_TOTAL SW_USED SW_FREE +do + +# Use the bc utility in a here document to calculate +# the percentage of free and used swap space. + +PERCENT_USED=$(bc < $.$F1.. + +iostat $SWITCH $SECS $INTERVAL | egrep -v '[a-zA-Z]|^$' \ + | awk '{print $'$F1', $'$F2', $'$F3', $'$F4'}' \ + | while read FIRST SECOND THIRD FOURTH +do +if ((STATCOUNT == 1)) # Loop counter to get the second set +then # of data produced by "iostat" + + case $OS in # Show the results based on the UNIX flavor + AIX) + echo -e "\nUser part is ${FIRST}%" + echo "System part is ${SECOND}%" + echo "Idle part is ${THIRD}%" + echo -e "I/O wait state is ${FOURTH}%\n" + ;; + HP-UX|OpenBSD) + echo -e "\nUser part is ${FIRST}%" + echo "Nice part is ${SECOND}%" + echo "System part is ${THIRD}%" + echo -e "Idle time is ${FOURTH}%\n" + ;; + SunOS|Linux) + echo -e "\nUser part is ${FIRST}%" + echo "System part is ${SECOND}%" + echo "I/O Wait is ${THIRD}%" + echo -e "Idle time is ${FOURTH}%\n" + ;; + esac +fi +((STATCOUNT = STATCOUNT + 1)) # Increment the loop counter +done diff --git a/chapter19/iostat_loadmon.ksh b/chapter19/iostat_loadmon.ksh new file mode 100755 index 0000000..d034477 --- /dev/null +++ b/chapter19/iostat_loadmon.ksh @@ -0,0 +1,109 @@ +#!/bin/ksh +# +# SCRIPT: iostat_loadmon.ksh +# AUTHOR: Randy Michael +# DATE: 07/26/2007 +# REV: 1.0.P +# PLATFORM: AIX, HP-UX, Linux, and Solaris +# +# PURPOSE: This shell script take two samples of the CPU +# usage using the "iostat" command. The first set of +# data is an average since the last system reboot. The +# second set of data is an average over the sampling +# period, or $INTERVAL. The result of the data aquired +# during the sampling perion is shown to the user based +# on the UNIX operating system that this shell script is +# executing on. Different UNIX flavors have differing +# outputs and the fields vary too. +# +# REV LIST: +# +# +# set -n # Uncomment to check the script syntax without any execution +# set -x # Uncomment to debug this shell script +# +################################################### +############# DEFINE VARIABLES HERE ############### +################################################### + +SECONDS=300 # Defines the number of seconds for each sample +INTERVAL=2 # Defines the total number of sampling intervals +STATCOUNT=0 # Initialize a loop counter to 0, zero +OS=$(uname) # Defines the UNIX flavor + +################################################### +##### SETUP THE ENVIRONMENT FOR EACH OS HERE ###### +################################################### + +# These "F-numbers" point to the correct field in the +# command output for each UNIX flavor. + +case $OS in +AIX|HP-UX) SWITCH='-t' + F1=3 + F2=4 + F3=5 + F4=6 + echo "\nThe Operating System is $OS\n" + ;; +Linux|SunOS) SWITCH='-c' + F1=1 + F2=2 + F3=3 + F4=4 + echo "\nThe Operating System is $OS\n" + ;; + +*) echo "\nERROR: $OS is not a supported operating system\n" + echo "\n\t...EXITING...\n" + exit 1 + ;; +esac + +################################################### +######## BEGIN GATHERING STATISTICS HERE ########## +################################################### + +echo "Gathering CPU Statistics using vmstat...\n" +echo "There are $INTERVAL sampling periods with" +echo "each interval lasting $SECONDS seconds" +echo "\n...Please wait while gathering statistics...\n" + +# Use "iostat" to monitor the CPU utilization and +# remove all lines that contain alphabetic characters +# and blank spaces. Then use the previously defined +# field numbers, for example F1=4,to point directly +# to the 4th position, for this example. The syntax +# for this techniques is ==> $'$F1'. + +iostat $SWITCH $SECONDS $INTERVAL | egrep -v '[a-zA-Z]|^$' \ + | awk '{print $'$F1', $'$F2', $'$F3', $'$F4'}' \ + | while read FIRST SECOND THIRD FORTH +do + if ((STATCOUNT == 1)) # Loop counter to get the second set + then # of data produces by "iostat" + + case $OS in # Show the results based on the UNIX flavor + AIX) + echo "\nUser part is ${FIRST}%" + echo "System part is ${SECOND}%" + echo "Idle part is ${THIRD}%" + echo "I/O wait state is ${FORTH}%\n" + ;; + HP-UX|Linux) + echo "\nUser part is ${FIRST}%" + echo "Nice part is ${SECOND}%" + echo "System part is ${THIRD}%" + echo "Idle time is ${FORTH}%\n" + ;; + SunOS) + echo "\nUser part is ${FIRST}%" + echo "System part is ${SECOND}%" + echo "I/O Wait is ${THIRD}%" + echo "Idle time is ${FORTH}%\n" + ;; + esac + + fi + ((STATCOUNT = STATCOUNT + 1)) # Increment the loop counter +done diff --git a/chapter19/sar_loadmon.ksh b/chapter19/sar_loadmon.ksh new file mode 100755 index 0000000..91e0f7e --- /dev/null +++ b/chapter19/sar_loadmon.ksh @@ -0,0 +1,92 @@ +#!/bin/ksh +# +# SCRIPT: sar_loadmon.ksh +# AUTHOR: Randy Michael +# DATE: 07/26/2007 +# REV: 1.0.P +# PLATFORM: AIX, HP-UX, Linux, and Solaris +# +# PURPOSE: This shell script take multiple samples of the CPU +# usage using the "sar" command. The average or +# sample periods is shown to the user based on the +# UNIX operating system that this shell script is +# executing on. Different UNIX flavors have differing +# outputs and the fields vary too. +# +# REV LIST: +# +# +# set -n # Uncomment to check the script syntax without any execution +# set -x # Uncomment to debug this shell script +# +################################################### +############# DEFINE VARIABLES HERE ############### +################################################### + +SECONDS=30 # Defines the number of seconds for each sample +INTERVAL=10 # Defines the total number of sampling intervals +OS=$(uname) # Defines the UNIX flavor + +################################################### +##### SETUP THE ENVIRONMENT FOR EACH OS HERE ###### +################################################### + +# These "F-numbers" point to the correct field in the +# command output for each UNIX flavor. + +case $OS in +AIX|HP-UX|SunOS) + F1=2 + F2=3 + F3=4 + F4=5 + echo "\nThe Operating System is $OS\n" + ;; +Linux) + F1=3 + F2=4 + F3=5 + F4=6 + echo "\nThe Operating System is $OS\n" + ;; +*) echo "\nERROR: $OS is not a supported operating system\n" + echo "\n\t...EXITING...\n" + exit 1 + ;; +esac + +################################################### +######## BEGIN GATHERING STATISTICS HERE ########## +################################################### + +echo "Gathering CPU Statistics using sar...\n" +echo "There are $INTERVAL sampling periods with" +echo "each interval lasting $SECONDS seconds" +echo "\n...Please wait while gathering statistics...\n" + +# This "sar" command take $INTERVAL samples, each lasting +# $SECONDS seconds. The average of this output is captured. + +sar $SECONDS $INTERVAL | grep Average \ + | awk '{print $'$F1', $'$F2', $'$F3', $'$F4'}' \ + | while read FIRST SECOND THIRD FORTH +do + # Based on the UNIX Flavor, tell the user the + # result of the statistics gathered. + + case $OS in + AIX|HP-UX|SunOS) + echo "\nUser part is ${FIRST}%" + echo "System part is ${SECOND}%" + echo "I/O wait state is ${FORTH}%" + echo "Idle time is ${FORTH}%\n" + ;; + Linux) + echo "\nUser part is ${FIRST}%" + echo "Nice part is ${SECOND}%" + echo "System part is ${THIRD}%" + echo "Idle time is ${FORTH}%\n" + ;; + esac +done + diff --git a/chapter19/uptime_fieldtest.ksh b/chapter19/uptime_fieldtest.ksh new file mode 100755 index 0000000..8b8f577 --- /dev/null +++ b/chapter19/uptime_fieldtest.ksh @@ -0,0 +1,41 @@ +#!/bin/ksh +# +# SCRIPT: uptime_fieldtest.ksh +# AUTHOR: Randy Michael +# DATE: 07/28/2002 +# PLATFORM: Any UNIX +# PURPOSE: This shell script is used to demonstrate how the +# average load statistics field shift depending on +# how long it has been since the last system reboot. +# The options are "min", "day", "hr" and combinations. +# If all other tests fail then the system has been running +# for 1-23 hours. + +echo "\n" # Write one blank new line to the screen + +# Show a current uptime output + +uptime + +# Find the correct field bases on how long the system has been up. + +if $(uptime | grep day | grep min >/dev/null) +then + FIELD=11 +elif $(uptime | grep day | grep hr >/dev/null) +then + FIELD=11 +elif $(uptime | grep day >/dev/null) +then + FIELD=10 +elif $(uptime | grep min >/dev/null) +then + FIELD=9 +else + FIELD=8 +fi + +# Display the correct field. + +echo "\nField is $FIELD \n" + diff --git a/chapter19/uptime_loadmon.bash b/chapter19/uptime_loadmon.bash new file mode 100755 index 0000000..9aadb6a --- /dev/null +++ b/chapter19/uptime_loadmon.bash @@ -0,0 +1,97 @@ +#!/bin/bash +# +# SCRIPT: uptime_loadmon.bash +# AUTHOR: Randy Michael +# DATE: 12/16/2007 +# REV: 1.0.P +# PLATFORM: AIX, HP-UX, Linux, OpenBSD, and Solaris +# +# PURPOSE: This shell script uses the "uptime" command to +# extract the most current load average data, which +# in this case is the average number of jobs in the +# run queue. +# +# set -x # Uncomment to debug this shell script +# set -n # Uncomment to check script syntax without any execution +# +################################################### +############# DEFINE VARIABLES HERE ############### +################################################### + +MAXLOAD=2.00 + +# Extract the interger and decimal parts of $MAXLOAD +MAXLOAD_INT=$(echo $MAXLOAD | awk -F '.' '{print $1}') +MAXLOAD_DEC=$(echo $MAXLOAD | awk -F '.' '{print $2}') + +# Check the UNIX flavor for the correct uptime values +# AIX specifies load as the last 5, 10, and 15 minutes. +# The other UNIX flavors specifies the load in the last +# 1, 5, and 15 minutes. + +case $(uname) in +AIX) L1=5 + L2=10 + L3=15 + ;; + *) + L1=1 + L2=5 + L3=15 + ;; +esac + + +################################################### +# DEFINE FUNCTIONS HERE +################################################### + +function get_max +{ +# This function return the number of auguments +# presented to the function +# +(($# == 0)) && return -1 +echo $# +} + +################################################### +# BEGINNING OF MAIN +################################################### + +echo -e "\nGathering System Load Average using the \"uptime\" command\n" + +# This next command statement extracts the latest +# load statistics no matter what the UNIX flavor is. + +NUM_ARGS=$(get_max $(uptime)) # Get the total number of fields in uptime output + +((NUM_ARGS == -1)) && echo "ERROR: get_max Function Error...EXITING..."\ + && exit 2 + + +# Extract the data for the last 5, 10, and 15 minutes + +ARGM2=$(((NUM_ARGS - 2))) # Subtract 2 from the total +ARGM1=$(((NUM_ARGS - 1))) # Subtract 1 from the total +ARGM=$NUM_ARGS # Last value in string + +uptime | sed s/,//g | awk '{print $'$ARGM2', $'$ARGM1', $'$ARGM'}' \ + | while read LAST5 LAST10 LAST15 +do + echo $LAST5 | awk -F '.' '{print $1, $2}' \ + | while read INT DEC + do + if (( INT > MAXLOAD_INT )) + then + echo -e "\nWARNING: System load has \ +reached ${LAST5}\n" + fi + + echo "System load average for the last $L1 minutes is $LAST5" + echo "System load average for the last $L2 minutes is $LAST10" + echo "System load average for the last $L3 minutes is $LAST15" + echo -e "\nThe load threshold is set to ${MAXLOAD}\n" + done +done + diff --git a/chapter19/uptime_loadmon.ksh b/chapter19/uptime_loadmon.ksh new file mode 100755 index 0000000..cb4b445 --- /dev/null +++ b/chapter19/uptime_loadmon.ksh @@ -0,0 +1,76 @@ +#!/bin/ksh +# +# SCRIPT: uptime_loadmon.ksh +# AUTHOR: Randy Michael +# DATE: 07/26/2007 +# REV: 1.0.P +# PLATFORM: AIX, HP-UX, Linux, and Solaris +# +# PURPOSE: This shell script uses the "uptime" command to +# extract the most current load average data. There +# is a special need in this script to determine +# how long the system has been running since the +# last reboot. The load average field "floats" +# during the first 24 hours after a system restart. +# +# set -x # Uncomment to debug this shell script +# set -n # Uncomment to check script syntax without any execution +# +################################################### +############# DEFINE VARIABLES HERE ############### +################################################### + +SECONDS=30 +INTERVAL=2 +MAXLOAD=2.00 +typeset -i INT_MAXLOAD=$MAXLOAD + +# Find the correct field to extract based on how long +# the system has been up, or since the last reboot. + +if $(uptime | grep day | grep min >/dev/null) +then + FIELD=11 +elif $(uptime | grep day | grep hrs >/dev/null) +then + FIELD=11 +elif $(uptime | grep day >/dev/null) +then + FIELD=10 +elif $(uptime | grep min >/dev/null) +then + FIELD=9 +else + FIELD=8 +fi + +################################################### +######## BEGIN GATHERING STATISTICS HERE ########## +################################################### + +echo "\nGathering System Load Average using the \"uptime\" command\n" + +# This next command statement extracts the latest +# load statistics no matter what the UNIX flavor is. + +LOAD=$(uptime | sed s/,//g | awk '{print $'$FIELD'}') + +# We need an integer representation of the $LOAD +# variable to do the test for the load going over +# the set threshold defince by the $INT_MAXLOAD +# variable + +typeset -i INT_LOAD=$LOAD + +# If the current load has exceeded the threshold then +# issue a warning message. The next step always shows +# the user what the current load and threshold values +# are set to. + +((INT_LOAD >= INT_MAXLOAD)) && echo "\nWARNING: System load has \ +reached ${LOAD}\n" + +echo "\nSystem load value is currently at ${LOAD}" +echo "The load threshold is set to ${MAXLOAD}\n" + + diff --git a/chapter19/vmstat_loadmon.bash b/chapter19/vmstat_loadmon.bash new file mode 100755 index 0000000..d9a4e7d --- /dev/null +++ b/chapter19/vmstat_loadmon.bash @@ -0,0 +1,123 @@ +#!/bin/bash +# +# SCRIPT: vmstat_loadmon.bash +# AUTHOR: Randy Michael +# DATE: 12/20/2007 +# REV: 1.0.P +# PLATFORM: AIX, HP-UX, Linux, OpenBSD, and Solaris +# +# PURPOSE: This shell script takes two samples of the CPU +# usage using the command. The first set of +# data is an average since the last system reboot. The +# second set of data is an average over the sampling +# period, or $INTERVAL. The result of the data acquired +# during the sampling period is shown to the user based +# on the UNIX operating system that this shell script is +# executing on. Different UNIX flavors have differing +# outputs and the fields vary too. +# +# REV LIST: +# +# +# set -n # Uncomment to check the script syntax without any execution +# set -x # Uncomment to debug this shell script +# +################################################### +############# DEFINE VARIABLES HERE ############### +################################################### + +SECS=300 # Defines the number of seconds for each sample +INTERVAL=2 # Defines the total number of sampling intervals +STATCOUNT=0 # Initializes a loop counter to 0, zero +OS=$(uname) # Defines the UNIX flavor + +################################################### +##### SET UP THE ENVIRONMENT FOR EACH OS HERE ###### +################################################### + +# These "F-numbers" point to the correct field in the +# command output for each UNIX flavor. + +OS=$(uname) + +case $OS in +AIX) + F1=14 + F2=15 + F3=16 + F4=17 + echo -e "\nThe Operating System is $OS\n" + ;; +HP-UX) + F1=16 + F2=17 + F3=18 + F4=1 # This "F4=1" is bogus and not used for HP-UX + echo -e "\nThe Operating System is $OS\n" + ;; +Linux) + F1=13 + F2=14 + F3=15 + F4=16 + echo -e "\nThe Operating System is $OS\n" + ;; +OpenBSD) + F1=17 + F2=18 + F3=19 + F4=1 # This "F4=1" is bogus and not used for Linux + echo -e "\nThe Operating System is $OS\n" + ;; +SunOS) + F1=20 + F2=21 + F3=22 + F4=1 # This "F4=1" is bogus and not used for SunOS + echo -e "\nThe Operating System is $OS\n" + ;; +*) echo -e "\nERROR: $OS is not a supported operating system\n" + echo -e "\n\t...EXITING...\n" + exit 1 + ;; +esac +################################################### +######## BEGIN GATHERING STATISTICS HERE ########## +################################################### + +echo -e "Gathering CPU Statistics using vmstat...\n" +echo "There are $INTERVAL sampling periods with" +echo "each interval lasting $SECS seconds" +echo -e "\n...Please wait while gathering statistics...\n" + +# Use "vmstat" to montor the CPU utilization and +# remove all lines that contain alphabetic characters +# and blank spaces. Then use the previously defined +# field numbers, for example F1=20,to point directly +# to the 20th position, for this example. The syntax +# for this technique is ==> $.$F1. and points directly +# to the $20 positional parameter. + +vmstat $SECS $INTERVAL | egrep -v '[a-zA-Z]|^$' \ + | awk '{print $'$F1', $'$F2', $'$F3', $'$F4'}' \ + | while read FIRST SECOND THIRD FOURTH +do + if ((STATCOUNT == 1)) # Loop counter to get the second set + then # of data produced by + + case $OS in # Show the results based on the UNIX flavor + AIX|Linux) + echo -e "\nUser part is ${FIRST}%" + echo "System part is ${SECOND}%" + echo "Idle part is ${THIRD}%" + echo -e "I/O wait state is ${FOURTH}%\n" + ;; + HP-UX|OpenBSD|SunOS) + echo -e "\nUser part is ${FIRST}%" + echo "System part is ${SECOND}%" + echo -e "Idle time is ${THIRD}%\n" + ;; + esac + fi +((STATCOUNT = STATCOUNT + 1)) # Increment the loop counter +done diff --git a/chapter19/vmstat_loadmon.ksh b/chapter19/vmstat_loadmon.ksh new file mode 100755 index 0000000..3c793bb --- /dev/null +++ b/chapter19/vmstat_loadmon.ksh @@ -0,0 +1,120 @@ +#!/bin/ksh +# +# SCRIPT: vmstat_loadmon.ksh +# AUTHOR: Randy Michael +# DATE: 07/26/2002 +# REV: 1.0.P +# PLATFORM: AIX, HP-UX, Linux, and Solaris +# +# PURPOSE: This shell script take two samples of the CPU +# usage using the "vmstat" command. The first set of +# data is an average since the last system reboot. The +# second set of data is an average over the sampling +# period, or $INTERVAL. The result of the data aquired +# during the sampling perion is shown to the user based +# on the UNIX operating system that this shell script is +# executing on. Different UNIX flavors have differing +# outputs and the fields vary too. +# +# REV LIST: +# +# +# set -n # Uncomment to check the script syntax without any execution +# set -x # Uncomment to debug this shell script +# +################################################### +############# DEFINE VARIABLES HERE ############### +################################################### + +SECONDS=300 # Defines the number of seconds for each sample +INTERVAL=2 # Defines the total number of sampling intervals +STATCOUNT=0 # Initialize a loop counter to 0, zero +OS=$(uname) # Defines the UNIX flavor + +################################################### +##### SETUP THE ENVIRONMENT FOR EACH OS HERE ###### +################################################### + +# These "F-numbers" point to the correct field in the +# command output for each UNIX flavor. + +case $OS in +AIX) # AIX has four relative columns in the output + F1=14 + F2=15 + F3=16 + F4=17 + + echo "\nThe Operating System is $OS\n" + ;; +HP-UX) # HP-UX only has three relative columns in the output + F1=16 + F2=17 + F3=18 + F4=1 # This "F4=1" is bogus and not used for HP-UX + + echo "\nThe Operating System is $OS\n" + ;; +Linux) # Linux only has three relative columns in the output + F1=14 + F2=15 + F3=16 + F4=1 # This "F4=1" is bogus and not used for Linux + + echo "\nThe Operating System is $OS\n" + ;; +SunOS) # SunOS only has three relative columns in the output + F1=20 + F2=21 + F3=22 + F4=1 # This "F4=1" is bogus and not used for SunOS + + echo "\nThe Operating System is $OS\n" + ;; +*) echo "\nERROR: $OS is not a supported operating system\n" + echo "\n\t...EXITING...\n" + exit 1 + ;; +esac + +################################################### +######## BEGIN GATHERING STATISTICS HERE ########## +################################################### + +echo "Gathering CPU Statistics using vmstat...\n" +echo "There are $INTERVAL sampling periods with" +echo "each interval lasting $SECONDS seconds" +echo "\n...Please wait while gathering statistics...\n" + +# Use "vmstat" to monitor the CPU utilization and +# remove all lines that contain alphabetic characters +# and blank spaces. Then use the previously defined +# field numbers, for example F1=20,to point directly +# to the 20th position, for this example. The syntax +# for this techniques is ==> $'$F1', and points directly +# to the $20 positional parameter. + +vmstat $SECONDS $INTERVAL | egrep -v '[a-zA-Z]|^$' \ + | awk '{print $'$F1', $'$F2', $'$F3', $'$F4'}' \ + | while read FIRST SECOND THIRD FORTH +do + if ((STATCOUNT == 1)) # Loop counter to get the second set + then # of data produces by "vmstat" + + case $OS in # Show the results based on the UNIX flavor + AIX) + echo "\nUser part is ${FIRST}%" + echo "System part is ${SECOND}%" + echo "Idle part is ${THIRD}%" + echo "I/O wait state is ${FORTH}%\n" + ;; + HP-UX|Linux|SunOS) + echo "\nUser part is ${FIRST}%" + echo "System part is ${SECOND}%" + echo "Idle time is ${THIRD}%\n" + ;; + esac + + fi + ((STATCOUNT = STATCOUNT + 1)) # Increment the loop counter +done diff --git a/chapter2/24_ways_to_parse.ksh b/chapter2/24_ways_to_parse.ksh new file mode 100755 index 0000000..763d79a --- /dev/null +++ b/chapter2/24_ways_to_parse.ksh @@ -0,0 +1,824 @@ +#!/usr/bin/ksh +# +# SCRIPT: 24_ways_to_parse.ksh +# AUTHOR: Randy Michael +# DATE: 08/15/2007 +# REV: 2.5.Q +# +# PURPOSE: This script shows the different ways of reading +# a file line-by-line. Again there is not just one way +# to read a file line-by-line and some are faster than +# others and some are more intuitive than others. +# +# REV LIST: +# +# 03/15/2007 - Randy Michael +# Set each of the while loops up as functions and the timing +# of each function to see which one is the fastest. +# +############### +# +# 08/10/2007 - Randy Michael +# Modified this script to include a total of 24 functions +# to parse a file line-by-line. +# +####################################################################### +# +# NOTE: To output the timing to a file use the following syntax: +# +# 24_ways_to_parse.ksh file_to_process > output_file_name 2>&1 +# +# The actual timing data is sent to standard error, file +# descriptor (2), and the function name header is sent +# to standard output, file descriptor (1). +# +####################################################################### +# +# set -n # Uncomment to check command syntax without any execution +# set -x # Uncomment to debug this script +# + +INFILE="$1" +OUTFILE=writefile.out +TIMEFILE="/tmp/loopfile.out" +>$TIMEFILE +THIS_SCRIPT=$(basename $0) + +###################################### +function usage +{ +echo "\nUSAGE: $THIS_SCRIPT file_to_process\n" +echo "OR - To send the output to a file use: " +echo "\n$THIS_SCRIPT file_to_process > output_file_name 2>&1 \n" +exit 1 +} +###################################### + +function verify_files +{ +diff $INFILE $OUTFILE >/dev/null 2>&1 +if (( $? != 0 )) +then + echo "ERROR: $INFILE and $OUTFILE do not match" + ls -l $INFILE $OUTFILE +fi +} +###################################### + +function cat_while_read_LINE +{ +# Method 1 + +# Zero out the $OUTFILE + +>$OUTFILE + +cat $INFILE | while read LINE +do + echo "$LINE" >> $OUTFILE + : +done +} +###################################### + +function while_read_LINE_bottom +{ +# Method 2 + +# Zero out the $OUTFILE + +>$OUTFILE + +while read LINE +do + echo "$LINE" >> $OUTFILE + : +done < $INFILE +} +###################################### + +function cat_while_LINE_line +{ +# Method 3 + +# Zero out the $OUTFILE + +>$OUTFILE + +cat $INFILE | while LINE=`line` +do + echo "$LINE" >> $OUTFILE + : +done +} +###################################### + +function while_LINE_line_bottom +{ +# Method 4 + +# Zero out the $OUTFILE + +>$OUTFILE + +while LINE=`line` +do + echo "$LINE" >> $OUTFILE + : + +done < $INFILE +} +###################################### + +function cat_while_LINE_line_cmdsub2 +{ +# Method 5 + +# Zero out the $OUTFILE + +>$OUTFILE + +cat $INFILE | while LINE=$(line) +do + echo "$LINE" >> $OUTFILE + : +done +} +###################################### + +function while_LINE_line_bottom_cmdsub2 +{ +# Method 6 + +# Zero out the $OUTFILE + +>$OUTFILE + +while LINE=$(line) +do + echo "$LINE" >> $OUTFILE +: +done < $INFILE +} +###################################### + +for_LINE_cat_FILE () +{ +# Method 7 + +# Zero out the $OUTFILE + +>$OUTFILE + +for LINE in `cat $INFILE` +do + echo "$LINE" >> $OUTFILE + : +done +} +###################################### + +for_LINE_cat_FILE_cmdsub2 () +{ +# Method 8 + +# Zero out the $OUTFILE + +>$OUTFILE + +for LINE in $(cat $INFILE) +do + echo "$LINE" >> $OUTFILE + : +done +} +##################################### + +while_line_outfile () +{ +# Method 9 + +# Zero out the $OUTFILE + +>$OUTFILE + +# This function processes every other +# line of the $INFILE, so do not use +# this method + +while read +do + line >>$OUTFILE + : +done < $INFILE +} +##################################### + +function while_read_LINE_FD_IN +{ +# Method 10 + +# Zero out the $OUTFILE +>$OUTFILE + +# Associate standard input with file descriptor 3 +# and redirect standard input to $INFILE + +exec 3<&0 +exec 0< $INFILE + +while read LINE +do + echo "$LINE" >> $OUTFILE + : +done + +# Restore standard input and close file +# descriptor 3 + +exec 0<&3 +exec 3>&- +} +###################################### + +function cat_while_read_LINE_FD_OUT +{ +# Method 11 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +cat $INFILE | while read LINE +do + echo "$LINE" + : +done + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- +} +###################################### + +function while_read_LINE_bottom_FD_OUT +{ +# Method 12 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +while read LINE +do + echo "$LINE" + : +done < $INFILE + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- +} +###################################### + +function while_LINE_line_bottom_FD_OUT +{ +# Method 13 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +while LINE=`line` +do + echo "$LINE" + : +done < $INFILE + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- +} +###################################### + +function while_LINE_line_bottom_cmdsub2_FD_OUT +{ +# Method 14 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +while LINE=$(line) +do + echo "$LINE" + : +done < $INFILE + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- +} +###################################### + +for_LINE_cat_FILE_FD_OUT () +{ +# Method 15 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +for LINE in `cat $INFILE` +do + echo "$LINE" + : +done + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- +} +###################################### + +for_LINE_cat_FILE_cmdsub2_FD_OUT () +{ +# Method 16 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +for LINE in $(cat $INFILE) +do + echo "$LINE" + : +done + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- +} +##################################### + +while_line_outfile_FD_IN () +{ +# Method 17 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard input with file descriptor 3 +# and redirect standard input to $INFILE + +exec 3<&0 +exec 0< $INFILE + +# This function processes every other +# line of the $INFILE, so do not use +# this method + +while read +do + line >> $OUTFILE + : +done + +# Restore standard input and close file +# descriptor 3 + +exec 0<&3 +exec 3>&- +} +##################################### + +while_line_outfile_FD_OUT () +{ +# Method 18 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +# This function processes every other +# line of the $INFILE, so do not use +# this method + +while read +do + line + : +done < $INFILE + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- +} +##################################### + +while_line_outfile_FD_IN_AND_OUT () +{ +# Method 19 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard input with file descriptor 3 +# and redirect standard input to $INFILE + +exec 3<&0 +exec 0< $INFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +while read +do + line + : +done + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- + +# Restore standard input and close file +# descriptor 3 + +exec 0<&3 +exec 3>&- +} +##################################### + +function while_LINE_line_FD_IN +{ +# Method 20 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard input with file descriptor 3 +# and redirect standard input to $INFILE + +exec 3<&0 +exec 0< $INFILE + +while LINE=`line` +do + echo "$LINE" >> $OUTFILE + : +done + +# Restore standard input and close file +# descriptor 3 + +exec 0<&3 +exec 3>&- +} +###################################### + +function while_LINE_line_cmdsub2_FD_IN +{ +# Method 21 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard input with file descriptor 3 +# and redirect standard input to $INFILE + +exec 3<&0 +exec 0< $INFILE + +while LINE=$(line) +do + echo "$LINE" >> $OUTFILE + : +done + +# Restore standard input and close file +# descriptor 3 + +exec 0<&3 +exec 3>&- +} +###################################### + +function while_read_LINE_FD_IN_AND_OUT +{ +# Method 22 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard input with file descriptor 3 +# and redirect standard input to $INFILE + +exec 3<&0 +exec 0< $INFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +while read LINE +do + echo "$LINE" + : +done + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- + +# Restore standard input and close file +# descriptor 3 + +exec 0<&3 +exec 3>&- +} +###################################### + +function while_LINE_line_FD_IN_AND_OUT +{ +# Method 23 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard input with file descriptor 3 +# and redirect standard input to $INFILE + +exec 3<&0 +exec 0< $INFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +while LINE=`line` +do + echo "$LINE" + : +done + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- + +# Restore standard input and close file +# descriptor 3 + +exec 0<&3 +exec 3>&- +} +###################################### + +function while_LINE_line_cmdsub2_FD_IN_AND_OUT +{ +# Method 24 + +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard input with file descriptor 3 +# and redirect standard input to $INFILE + +exec 3<&0 +exec 0< $INFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +while LINE=$(line) +do + echo "$LINE" + : +done + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- + +# Restore standard input and close file +# descriptor 3 + +exec 0<&3 +exec 3>&- +} + +###################################### +########### START OF MAIN ############ +###################################### + +# Test the Input + +# Looking for exactly one parameter +(( $# == 1 )) || usage + +# Does the file exist as a regular file? +[[ -f $1 ]] || usage + +echo "\nStarting File Processing of each Method\n" + +echo "Method 1:" +echo "function cat_while_read_LINE" +time cat_while_read_LINE +verify_files +sleep 1 +echo "\nMethod 2:" +echo "function while_read_LINE_bottom" +time while_read_LINE_bottom +verify_files +sleep 1 +echo "\nMethod 3:" +echo "function cat_while_LINE_line" +time cat_while_LINE_line +verify_files +sleep 1 +echo "\nMethod 4:" +echo "function while_LINE_line_bottom" +time while_LINE_line_bottom +verify_files +sleep 1 +echo "\nMethod 5:" +echo "function cat_while_LINE_line_cmdsub2" +time cat_while_LINE_line_cmdsub2 +verify_files +sleep 1 +echo "\nMethod 6:" +echo "function while_LINE_line_botton_cmdsub2" +time while_LINE_line_bottom_cmdsub2 +verify_files +sleep 1 +echo "\nMethod 7:" +echo "function for_LINE_cat_FILE" +time for_LINE_cat_FILE +verify_files +sleep 1 +echo "\nMethod 8:" +echo "function for_LINE_cat_FILE_cmdsub2" +time for_LINE_cat_FILE_cmdsub2 +verify_files +sleep 1 +echo "\nMethod 9:" +echo "function while_line_outfile" +time while_line_outfile +verify_files +sleep 1 +echo "\nMethod 10:" +echo "function while_read_LINE_FD_IN" +time while_read_LINE_FD_IN +verify_files +sleep 1 +echo "\nMethod 11:" +echo "function cat_while_read_LINE_FD_OUT" +time cat_while_read_LINE_FD_OUT +verify_files +sleep 1 +echo "\nMethod 12:" +echo "function while_read_LINE_bottom_FD_OUT" +time while_read_LINE_bottom_FD_OUT +verify_files +sleep 1 +echo "\nMethod 13:" +echo "function while_LINE_line_bottom_FD_OUT" +time while_LINE_line_bottom_FD_OUT +verify_files +sleep 1 +echo "\nMethod 14:" +echo "function while_LINE_line_bottom_cmdsub2_FD_OUT" +time while_LINE_line_bottom_cmdsub2_FD_OUT +verify_files +sleep 1 +echo "\nMethod 15:" +echo "function for_LINE_cat_FILE_FD_OUT" +time for_LINE_cat_FILE_FD_OUT +verify_files +sleep 1 +echo "\nMethod 16:" +echo "function for_LINE_cat_FILE_cmdsub2_FD_OUT" +time for_LINE_cat_FILE_cmdsub2_FD_OUT +verify_files +sleep 1 +echo "\nMethod 17:" +echo "function while_line_outfile_FD_IN" +time while_line_outfile_FD_IN +verify_files +sleep 1 +echo "\nMethod 18:" +echo "function while_line_outfile_FD_OUT" +time while_line_outfile_FD_OUT +verify_files +sleep 1 +echo "\nMethod 19:" +echo "function while_line_outfile_FD_IN_AND_OUT" +time while_line_outfile_FD_IN_AND_OUT +verify_files +sleep 1 +echo "\nMethod 20:" +echo "function while_LINE_line_FD_IN" +time while_LINE_line_FD_IN +verify_files +sleep 1 +echo "\nMethod 21:" +echo "function while_LINE_line_cmdsub2_FD_IN" +time while_LINE_line_cmdsub2_FD_IN +verify_files +sleep 1 +echo "\nMethod 22:" +echo "function while_read_LINE_FD_IN_AND_OUT" +time while_read_LINE_FD_IN_AND_OUT +verify_files +sleep 1 +echo "\nMethod 23:" +echo "function while_LINE_line_FD_IN_AND_OUT" +time while_LINE_line_FD_IN_AND_OUT +verify_files +sleep 1 +echo "\nMethod 24:" +echo "function while_LINE_line_cmdsub2_FD_IN_AND_OUT" +time while_LINE_line_cmdsub2_FD_IN_AND_OUT +verify_files + diff --git a/chapter2/function_build_random_line b/chapter2/function_build_random_line new file mode 100755 index 0000000..083cea3 --- /dev/null +++ b/chapter2/function_build_random_line @@ -0,0 +1,19 @@ +build_random_line () +{ +# This function extracts random characters +# from the KEYS array by using the RANDOM +# shell variable + +C=1 +LINE= +until (( C > 79 )) +do + LINE="${LINE}${KEYS[$(($RANDOM % X + 1))]}" + (( C = C + 1 )) +done + +# Return the line of random characters + +echo "$LINE" +} + diff --git a/chapter2/function_elapsed_time b/chapter2/function_elapsed_time new file mode 100755 index 0000000..c07454e --- /dev/null +++ b/chapter2/function_elapsed_time @@ -0,0 +1,17 @@ +elasped_time () +{ +# This elapsed_time function is for Bash shell + +SEC=$1 + +(( SEC < 60 )) && echo -e "[Elasped time: \ +$SEC seconds]\c" + +(( SEC >= 60 && SEC < 3600 )) && echo -e \ +"[Elasped time: $(( SEC / 60 )) min $(( SEC % 60 )) sec]\c" + +(( SEC > 3600 )) && echo -e "[Elasped time: \ +$(( SEC / 3600 )) hr $(( (SEC % 3600) / 60 )) min \ +$(( (SEC % 3600) % 60 )) sec]\c" +} + diff --git a/chapter2/function_load_default_keyboard b/chapter2/function_load_default_keyboard new file mode 100755 index 0000000..c774358 --- /dev/null +++ b/chapter2/function_load_default_keyboard @@ -0,0 +1,15 @@ +load_default_keyboard () +{ +# Loop through each character in the following list and +# append each character to the $CHAR_FILE file. This +# produces a file with one character on each line. + +for CHAR in 1 2 3 4 5 6 7 8 9 0 q w e r t y u i o \ + p a s d f g h j k l z x c v b n m \ + Q W E R T Y U I O P A S D F G H J K L \ + Z X C V B N M 0 1 2 3 4 5 6 7 8 9 +do + echo "$CHAR" >> $CHAR_FILE +done +} + diff --git a/chapter2/random_file.bash b/chapter2/random_file.bash new file mode 100755 index 0000000..8966eeb --- /dev/null +++ b/chapter2/random_file.bash @@ -0,0 +1,204 @@ +#!/bin/bash +# +# SCRIPT: random_file.bash +# AUTHOR: Randy Michael +# DATE: 8/3/2007 +# REV: 1.0 +# PLATFORM: Not Platform Dependent +# +# PURPOSE: This script is used to create a +# specific size file of random characters. +# The methods used in this script include +# loading an array with alphanumeric +# characters, then using the /dev/random +# character special file to seed the RANDOM +# shell variable, which in turn is used to +# extract a random set of characters from the +# KEYS array to build the OUTFILE of a +# specified MB size. +# +# set -x # Uncomment to debug this script +# +# set -n # Uncomment to check script syntax +# # without aby execution. Do not forget +# # to put the comment back in or the +# # script will never execute. +# +########################################## +# DEFINE FILES AND VARIABLES HERE +########################################## + +typeset -i MB_SIZE=$1 +typeset -i RN +typeset -i i=1 +typeset -i X=0 +WORKDIR=/scripts +OUTFILE=${WORKDIR}/largefile.random.txt +>$OUTFILE +THIS_SCRIPT=$(basename $0) +CHAR_FILE=${WORKDIR}/char_file.txt + +########################################## +# DEFINE FUNCTIONS HERE +########################################## + +build_random_line () +{ +# This function extracts random characters +# from the KEYS array by using the RANDOM +# shell variable + +C=1 +LINE= +until (( C > 79 )) +do + LINE="${LINE}${KEYS[$(($RANDOM % X + 1))]}" + (( C = C + 1 )) +done + +# Return the line of random characters + +echo "$LINE" +} + +########################################## + +elasped_time () +{ +SEC=$1 + +(( SEC < 60 )) && echo -e "[Elasped time: \ +$SEC seconds]\c" + +(( SEC >= 60 && SEC < 3600 )) && echo -e \ +"[Elasped time: $(( SEC / 60 )) min $(( SEC % 60 )) sec]\c" + +(( SEC > 3600 )) && echo -e "[Elasped time: \ +$(( SEC / 3600 )) hr $(( (SEC % 3600) / 60 )) min \ +$(( (SEC % 3600) % 60 )) sec]\c" +} + +########################################## + +load_default_keyboard () +{ +# Loop through each character in the following list and +# append each character to the $CHAR_FILE file. This +# produces a file with one character on each line. + +for CHAR in 1 2 3 4 5 6 7 8 9 0 q w e r t y u i o \ + p a s d f g h j k l z x c v b n m \ + Q W E R T Y U I O P A S D F G H J K L \ + Z X C V B N M 0 1 2 3 4 5 6 7 8 9 +do + echo "$CHAR" >> $CHAR_FILE +done +} +########################################## + +usage () +{ +echo -e "\nUSAGE: $THIS_SCRIPT Mb_size" +echo -e "Where Mb_size is the size of the file to build\n" +} + +########################################## +# BEGINNING OF MAIN +########################################## + +if (( $# != 1 )) +then + usage + exit 1 +fi + +# Test for an integer value + +case $MB_SIZE in +[0-9]) : # Do nothing + ;; + *) usage + ;; +esac + +# Test for the $CHAR_FILE + +if [ ! -s "$CHAR_FILE" ] +then + echo -e "\nNOTE: $CHAR_FILE does not esist" + echo "Loading default keyboard data." + echo -e "Creating $CHAR_FILE...\c" + load_default_keyboard + echo "Done" +fi + +# Load Character Array + +echo -e "\nLoading array with alphanumeric character elements" + +while read ARRAY_ELEMENT +do + (( X = X + 1 )) + KEYS[$X]=$ARRAY_ELEMENT + +done < $CHAR_FILE + +echo "Total Array Character Elements: $X" + +# Use /dev/random to seed the shell variable RANDOM + +echo "Querying the kernel random number generator for a random seed" + +RN=$(dd if=/dev/random count=1 2>/dev/null \ + | od -t u4 | awk '{print $2}'| head -n 1) + +# The shell variable RANDOM is limited to 32767 + +echo "Reducing the random seed value to between 1 and 32767" + +RN=$(( RN % 32767 + 1 )) + +# Initialize RANDOM with a new seed + +echo "Assigning a new seed to the RANDOM shell variable" + +RANDOM=$RN + +echo "Building a $MB_SIZE MB random character file ==> $OUTFILE" +echo "Please be patient, this may take some time to complete..." +echo -e "Executing: .\c" + +# Reset the shell SECONDS variable to zero seconds. + +SECONDS=0 + +TOT_LINES=$(( MB_SIZE * 12800 )) + +until (( i > TOT_LINES )) +do + build_random_line >> $OUTFILE + (( $(( i % 100 )) == 0 )) && echo -e ".\c" + (( i = i + 1 )) +done + +# Capture the total seconds + +TOT_SEC=$SECONDS + +echo -e "\n\nSUCCESS: $OUTFILE created at $MB_SIZE MB\n" + +elasped_time $TOT_SEC + +# Calculate the bytes/second file creation rate + +(( MB_SEC = ( MB_SIZE * 1024000 ) / TOT_SEC )) + +echo -e "\n\nFile Creation Rate: $MB_SEC bytes/second\n" + +echo -e "File size:\n" +ls -l $OUTFILE +echo + +########################################## +# END OF random_file.bash SCRIPT +########################################## diff --git a/chapter20/stale_LV_mon.ksh b/chapter20/stale_LV_mon.ksh new file mode 100755 index 0000000..9875dbb --- /dev/null +++ b/chapter20/stale_LV_mon.ksh @@ -0,0 +1,65 @@ +#!/bin/ksh +# +# SCRIPT: stale_LV_mon.ksh +# +# AUTHOR: Randy Michael +# DATE: 01/22/2007 +# REV: 1.1.P +# +# PLATFORM: AIX only +# +# PURPOSE: This shell script is used to query the system +# for stale PPs in every active LV within every active +# VG. +# +# REVISION LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check command syntax without any execution + +case $(uname) in +AIX) : # Correct OS + # NOTE: a (:) colon is a no-op in Korn shell + ;; + *) echo "\nERROR: This shell script will only work on AIX" + echo "...EXITING...\n" + exit 99 + ;; +esac + +THIS_HOST=`hostname` # Hostname of this machine +STALE_PP_COUNT=0 # Initialize to zero + +# Find all active VGs +echo "\nGathering a list of active Volume Groups" +ACTIVE_VG_LIST=$(lsvg -o) + +# Find all active LVs in every active VG. +echo "\nCreating a list of all active Logical Volume" +ACTIVE_LV_LIST=$(lsvg -l $ACTIVE_VG_LIST | grep open | awk '{print $1}') + +# Loop through each active LV and query for stale disk partitions +echo "\nLooping through each Logical Volume searching for stale PPs" +echo "...Please be patient this may take several minutes to complete..." + +for LV in $(echo $ACTIVE_LV_LIST) +do + # Extract the number of STALE PPs for each active LV + NUM_STALE_PP=`lslv -L $LV | grep "STALE PP" | awk '{print $3}'` + # Check for a value greater than zero + if ((NUM_STALE_PP > 0)) + then + # Increment the stale PP counter + (( STALE_PP_COUNT = $STALE_PP_COUNT + 1)) + # Report on all LVs containing stale disk partitions + echo "\n${THIS_HOST}: $LV has $NUM_STALE_PP PPs" + fi +done + +# Give some feedback if no stale disk partition were found + +if ((STALE_PP_COUNT == 0)) +then + echo "\nNo stale PPs were found in any active LV...EXITING...\n" +fi diff --git a/chapter20/stale_PP_mon.ksh b/chapter20/stale_PP_mon.ksh new file mode 100755 index 0000000..f0160b7 --- /dev/null +++ b/chapter20/stale_PP_mon.ksh @@ -0,0 +1,77 @@ +#!/usr/bin/ksh +# +# SCRIPT: stale_PP_mon.ksh +# +# AUTHOR: Randy Michael +# DATE: 01/29/07 +# REV: 1.2.P +# +# PLATFORM: AIX only +# +# PURPOSE: This shell script is used to query the system for stale PPs. +# The method queries the system for all of the currently vaied-on +# volume groups and then builds a list of the PVs to query. If a PV +# query detects any stale partitions notification is sent to the +# screen. Each step in the process has user notification +# +# REVISION LIST: +# +# +# set -x # Uncomment to debug this shell script +# set -n # Uncomment to check command syntax without any execution + +case $(uname) in +AIX) : # Correct OS + # NOTE: a (:) colon is a no-op in Korn shell + ;; + *) echo "\nERROR: This shell script will only work on AIX" + echo "...EXITING...\n" + exit 99 + ;; +esac + +THIS_HOST=$(hostname) # Hostname of this machine +FIRST_TIME=0 # Initialize to zero +HDISK_LIST= # Initialize to NULL +STALE_PP_COUNT=0 # Initialize to zero + +# Infor the user at each step +echo "\nGathering a list of hdisks to query\n" + +# Loop through each currently varied-on VG + +for VG in $(lsvg -o) +do + # Build a list of hdisks that belong to currently varied on VGs + echo "Querying $VG for a list of disks" + HDISK_LIST="$HDISK_LIST $(lsvg -p $VG |grep disk | awk '{print $1}')" +done + +echo "\nStarting the hdisk query on individual disks\n" + +# Loop through each of the hdisks found in the previous loop + +for HDISK in $(echo $HDISK_LIST) +do + # Query a new hdisk on each loop iteration + + echo "Querying $HDISK for stale partitions" + NUM_STALE_PP=$(lspv -L $HDISK | grep "STALE PARTITIONS:" | awk '{print $3}') + # Check to see if the stale partition count is greater than zero + if ((NUM_STALE_PP > 0)) + then + # This hdisk has at least one stale partition - Report it! + echo "\n${THIS_HOST}: Disk $HDISK has $NUM_STALE_PP Stale Partitions" + if ((STALE_PP_COUNT == 0)) + then + # Build a list of hdisks that have stale disk partitions + STALE_HDISK_LIST=$(echo $STALE_HDISK_LIST; echo $HDISK) + + fi + fi +done + +# If no stale partitions were found send a "all is good message" + +((NUM_STALE_PP > 0)) || echo "\n${THIS_HOST}: No Stale PPs have been found...EXITING...\n" + diff --git a/chapter20/stale_VG_PV_LV_PP_mon.ksh b/chapter20/stale_VG_PV_LV_PP_mon.ksh new file mode 100755 index 0000000..693226a --- /dev/null +++ b/chapter20/stale_VG_PV_LV_PP_mon.ksh @@ -0,0 +1,217 @@ +#!/usr/bin/ksh +# +# SCRIPT: stale_VG_PV_LV_PP_mon.ksh +# +# AUTHOR: Randy Michael +# DATE: 01/29/2007 +# REV: 1.2.P +# +# PLATFORM: AIX only +# +# PURPOSE: This shell script is used to query the system for stale PPs. +# The method queries the system for all of the currently vaied-on +# volume groups and then builds a list of the PVs to query. If a PV +# query detects any stale partitions notification is sent to the +# screen. Each step in the process has user notification +# +# REVISION LIST: +# +# +# set -x # Uncomment to debug this shell script +# set -n # Uncomment to check command syntax without any execution +# +# EXIT CODES: 0 ==> Normal execution or no stale PP were found +# 1 ==> Trap EXIT +# 2 ==> Auto resyncing failed +# +#################################################### +######### DEFINE VARIABLES HERE #################### +#################################################### + +case $(uname) in +AIX) : # Correct OS + # NOTE: a (:) colon is a no-op in Korn shell + ;; + *) echo "\nERROR: This shell script will only work on AIX" + echo "...EXITING...\n" + exit 99 + ;; +esac + + +ATTEMPT_RESYNC=FALSE # Flag to enable auto resync, "TRUE" will resync + +LOGFILE="/tmp/stale_PP_log" # Stale PP logfile +THIS_HOST=$(hostname) # Hostname of this machine +STALE_PP_COUNT=0 # Initialize to zero +STALE_PV_COUNT=0 # Initialize to zero +HDISK_LIST= # Initialize to NULL +INACTIVE_PP_LIST= # Initialize to NULL +STALE_PV_LIST= # Initialize to NULL +STALE_LV_LIST= # Initialize to NULL +STALE_VG_LIST= # Initialize to NULL +RESYNC_LV_LIST= # Initialize to NULL +PV_LIST= # Initialize to NULL + +####################################### +#### INITIALIZE THE LOG FILE #### + +>$LOGFILE # Initialize the log file to empty +date >> $LOGFILE # Date the log file was created +echo "\n$THIS_HOST \n" >> $LOGFILE # Host name for this report + +#### DEFINE FUNCTIONS HERE ############ + +# Trap Exit function + +function trap_exit +{ +echo "\n\t...EXITING on a TRAPPED signal...\n" +} + +####################################### + +# Set a trap... + +trap 'trap_exit; exit 1' 1 2 3 5 15 + +####################################### +######### BEGINNING OF MAIN ########### +####################################### + +# Inform the user at each step + +# Loop through each currently varied-on VG and query VG for stale PVs. +# For any VG that has at least one stale PV we then query the VG +# for the list of associated PV and build the $PV_LIST + +echo "\nSearching each Volume Group for stale Physical Volumes...\c" \ + | tee -a $LOGFILE + +# Search each VG for stale PVs, then build a list of VGs and PVs +# that have stale disk partitions + +for VG in $(lsvg -o) +do + NUM_STALE_PV=$(lsvg $VG | grep 'STALE PVs:' | awk '{print $3}') + + if ((NUM_STALE_PV > 0)) + then + STALE_VG_LIST="$STALE_VG_LIST $VG" + PV_LIST="$PV_LIST $(lsvg -p $VG | tail +3 | awk '{print $1}')" + ((STALE_PV_COUNT = $STALE_PV_COUNT + 1)) + fi +done + +# Test to see if any stale PVs were found, if not then +# exit with return code 0 + +if ((STALE_PV_COUNT == 0)) +then + echo "\nNo Stale Disk Mirrors Found...EXITING...\n" | tee -a $LOGFILE + exit 0 +else + echo "\nStale Disk Mirrors Found!...Searching each hdisk for stale \ +PPs...\c" | tee -a $LOGFILE +fi + +# Now we have a list of PVs from every VG that reported stale PVs +# The next step is to query each PV to make sure each PV is in +# and "active" state and then query each PV for stale PPs. +# If a PV is found to be inactive then we will not query +# the PV for stale partitions, but move on to the next PV in +# the $PV_LIST. + +for HDISK in $(echo $PV_LIST) +do + PV_STATE=$(lspv $HDISK | grep 'PV STATE:' | awk '{print $3}') + if [[ $PV_STATE != 'active' ]] + then + INACTIVE_PV_LIST="$INACTIVE_PV_LIST $HDISK" + fi + if ! $(echo $INACTIVE_PV_LIST | grep $HDISK) >/dev/null 2>&1 + then + NUM_STALE_PP=$(lspv $HDISK | grep 'STALE PARTITIONS:' \ + | awk '{print $3}') + if ((NUM_STALE_PP > 0)) + then + STALE_PV_LIST="$STALE_PV_LIST $HDISK" + ((STALE_PP_COUNT = $STALE_PP_COUNT + 1)) + fi + fi +done + +# Now we have the list of PVs that contain the stale PPs. +# Next we want to get a list of all of the LVs affected. + +echo "\nSearching each disk with stale PPs for associated LVs\c" \ + | tee -a $LOGFILE + +for PV in $(echo $STALE_PV_LIST) +do + STALE_LV_LIST="$STALE_LV_LIST $(lspv -l $PV | tail +3 \ + | awk '{print $1}')" +done + +# Using the STALE_LV_LIST variable list we want to query +# each LV to find which ones need to be resynced + +echo "\nSearch each LV for stale partitions to build a resync LV list\c" \ + | tee -a $LOGFILE + +for LV in $(echo $STALE_LV_LIST) +do + LV_NUM_STALE_PP=$(lslv $LV | grep "STALE PPs:" | awk '{print $3}') + (($LV_NUM_STALE_PP == 0)) & RESYNC_LV_LIST="$RESYNC_LV_LIST $LV" +done + +# If any inactive PV were found we need to inform the user +# of each inactive PV + +# Check for a NULL variable + +if [[ -n $INACTIVE_PV_LIST && $INACTIVE_PV_LIST != '' ]] +then + for PV in $(echo $INACTIVE_PV_LIST) + do + echo "\nWARNING: Inactive Physical Volume Found:" | tee -a $LOGFILE + echo "\n$PV is currently inactive:\n" | tee -a $LOGFILE + echo "\nThis script is not suitable to to correct this problem..." \ + | tee -a $LOGFILE + echo " ...CALL IBM SUPPORT ABOUT ${PV}..." | tee -a $LOGFILE + done +fi + +echo "\nStale Partitions have been found on at least one disk!" \ + | tee -a $LOGFILE +echo "\nThe following Volume Group(s) have stale PVs:\n" \ + | tee -a $LOGFILE +echo $STALE_VG_LIST | tee -a $LOGFILE +echo "\nThe stale disk(s) involved include the following:\n" \ + | tee -a $LOGFILE +echo $STALE_PV_LIST | tee -a $LOGFILE +echo "\nThe following Logical Volumes need to be resynced:\n" \ + | tee -a $LOGFILE +echo $RESYNC_LV_LIST | tee -a $LOGFILE + +if [[ $ATTEMPT_RESYNC = "TRUE" ]] +then + echo "\nAttempting to resync the LVs on $RESYNC_PV_LIST ...\n" \ + | tee -a $LOGFILE + syncvg -l $RESYNC_LV_LIST | tee -a $LOGFILE 2>&1 + if (( $? == 0)) + then + echo "\nResyncing all of the LVs SUCCESSFUL...EXITING..." \ + | tee -a $LOGFILE + else + echo "\nResyncing FAILED...EXITING...\n" | tee -a $LOGFILE + exit 2 + fi +else + echo "\nAuto resync is not enabled...set to TRUE to automatically \ +resync\n" | tee -a $LOGFILE + echo "\n\t...EXITING...\n" | tee -a $LOGFILE +fi + +echo "\nThe log file is: $LOGFILE\n" + diff --git a/chapter21/SSAidentify.ksh b/chapter21/SSAidentify.ksh new file mode 100755 index 0000000..4717e01 --- /dev/null +++ b/chapter21/SSAidentify.ksh @@ -0,0 +1,507 @@ +#!/bin/ksh +# +# SCRIPT: SSAidentify.ksh +# +# AUTHOR: Randy Michael +# +# DATE: 11/7/2007 +# +# REV: 2.5.A +# +# PURPOSE: This script is used to turn on, or off, the +# identify lights on the system's SSA disks +# +# REV LIST: +# 11/27/2007: Added code to allow the user to turn on/off +# individual pdisk lights +# +# 12/10/2007: Added code to accept a combination of pdisks +# and hdisks. For each hdisk passed the script translates +# the hdisk# into the associated pdisk#(s). +# +# 12/10/2007: Added code to ALLOW using the currently VARIED ON +# Volume Group's disks (-v switch), as opposed to ALL DEFINED SSA disks, +# which is the default behavior. Very helpful in a HACMP environment. +# +# 12/11/2007: Added the "twirl" function to give the user feedback +# during long processing periods, i.e. translating a few hundred +# hdisks into associated pdisks. The twirl function is just a rotating +# cursor and it twirls during the translation processing. + + +# set -x # Uncomment to debug this script + +SCRIPTNAME=$(basename $0) + +############################################## + +function usage +{ +echo "\nUSAGE ERROR... +\nMAN PAGE ==> $SCRIPTNAME -? +\nTo Turn ALL Lights Either ON or OFF: +\nUSAGE: SSAidentify.ksh [-v] [on] [off] +EXAMPLE: SSAidentify.ksh -v on +\nWill turn ON ALL of the system's currently VARIED ON +SSA identify lights. NOTE: The default is all DEFINED SSA disks +\nTo Turn SPECIFIC LIGHTS Either ON or OFF Using EITHER +the pdisk#(s) AND/OR the hdisk#(s): +\nUSAGE: SSAidentify.ksh [on] [off] pdisk{#1} [hdisk{#2}]... +EXAMPLE: SSAidentify.ksh on hdisk36 pdisk44 pdisk47 +\nWill turn ON the lights to all of the associated pdisk#(s) +that hdisk36 translates to and PDISKS pdisk44 and pdisk47. +\nNOTE: Can use all pdisks, all hdisks or BOTH hdisk +and pdisk together if you want..." +exit 1 +} + +############################################## + +function man_page +{ +MAN_FILE="/tmp/man_file.out" +>$MAN_FILE + +# Text for the man page... + +echo "\n\t\tMAN PAGE FOR SSAidentify.ksh SHELL SCRIPT\n +This script is used to turn on, or off, the system's SSA disk drive +identification lights. You can use this script in the following ways:\n +To turn on/off ALL DEFINED SSA drive identification lights, ALL VARIED-ON SSA +drive identification lights (-v switch), AN INDIVIDUAL SSA drive identification +light or A LIST OF SSA drive identification lights.\n +SSA disk drives can be specified by EITHER the pdisk OR the hdisk, or +a COMBINATION OF BOTH. The script translates all hdisks into the +associated pdisk(s) using the system's /usr/sbin/ssaxlate command and turns +the SSA identification light on/off using the system's /usr/sbin/ssaidentify +command.\n + +This script has four switches that control its' action:\n +-? - Displays this man page.\n +on - Turns the SSA identify light(s) ON.\n +off - Turns the SSA identify light(s) OFF.\n +-v - Specifies to only act on SSA disks which are in currently varied-on +volume groups. The default action is to act on ALL DEFINED SSA disks.\n +NOTE: This switch is ignored for turning on/off individual SSA drive lights, +only valid when turning on/off ALL lights. This option is very helpful in an +HACMP environment since ALL DEFINED, the default action, will turn on/off all +of the SSA drive lights even if the SSA disk is in a volume group which is not +currently varied-on. This can be confusing in an HA cluster.\n +Using this script is very straight forward. The following examples show the +correct use of this script:\n" >> $MAN_FILE +echo "\nUSAGE: SSAidentify.ksh [-v] [on] [off] [pdisk#/hdisk#] [pdisk#/hdisk# list] +\n\nTo Turn ALL Lights Either ON or OFF: +\nUSAGE: SSAidentify.ksh [-v] [on] [off] +\nEXAMPLE: $SCRIPTNAME on +\nWill turn ON ALL of the system's DEFINED SSA identify lights. +This is the default. +EXAMPLE: SSAidentify.ksh -v on +\nWill turn ON ALL of the system's currently VARIED ON +SSA identify lights. OVERRIDES THE DEFAULT ACTION OF ALL DEFINED SSA DISKS +\nTo Turn SPECIFIC LIGHTS Either ON or OFF Using EITHER +the pdisk#(s) AND/OR the hdisk#(s): +\nUSAGE: $SCRIPTNAME [on] [off] pdisk{#1} [hdisk{#2}]... +\nEXAMPLE: $SCRIPTNAME on hdisk36 pdisk44 pdisk47 +\nWill turn ON the lights to all of the associated pdisk#(s) +that hdisk36 translates to and PDISKS pdisk44 and pdisk47. +\nNOTE: Can use all pdisks, all hdisks or BOTH hdisk +and pdisk together if you want...\n\n" >> $MAN_FILE + +more $MAN_FILE + +# End of man_page function +} + +############################################## + +function cleanup +{ +echo "\n...Exiting on a trapped signal...EXITING STAGE LEFT...\n" + +kill $TWIRL_PID + +# End of cleanup function +} + +############################################## + +function twirl +{ +TCOUNT="0" # For each TCOUNT the line twirls one increment + +while : # Loop forever...until you break out of the loop +do + TCOUNT=$(expr ${TCOUNT} + 1) # Increment the TCOUNT + case ${TCOUNT} in + "1") echo '-'"\b\c" + sleep 1 + ;; + "2") echo '\\'"\b\c" + sleep 1 + ;; + "3") echo "|\b\c" + sleep 1 + ;; + "4") echo "/\b\c" + sleep 1 + ;; + *) TCOUNT="0" ;; # Reset the TCOUNT to "0", zero. + esac +done +# End of twirl finction +} + +############################################ + +function kill_twirl +{ +kill `jobs -p` 2>/dev/null +# End of kill_twirl function +} + +############################################ +function all_defined_pdisks + +{ + # TURN ON/OFF ALL LIGHTS: + # Loop through each of the system.s pdisks by using the "lsdev" + # command with the "-Cc pdisk" switch while using "awk" to extract + # out the actual pdisk number. We will either + # turn the identifier lights on or off, specified by the + # $SWITCH variable: + # + # Turn lights on: -y + # Turn lights off: -n + # + # as the $SWITCH value to the "ssaidentify" command, as used below... + +echo "\nTurning $STATE ALL of the system.s pdisks...Please Wait...\n" + +for PDISK in $(lsdev -Cc pdisk -s ssar -H | awk .{print $1}. | grep pdisk) +do + echo "Turning $STATE ==> $PDISK" + ssaidentify -l $PDISK -${SWITCH} || echo "Turning $STATE $PDISK Failed" +done +echo "\n...TASK COMPLETE...\n" + +} + +############################################ + +function all_varied_on_pdisks +{ +trap 'kill -9 $TWIRL_PID; return 1' 1 2 3 15 + +cat /dev/null > $HDISKFILE + +echo "\nGathering a list of Varied on system SSA disks...Please wait...\c" + +VG_LIST=$(lsvg -o) # Get the list of Varied ON Volume Groups + +for VG in $(echo $VG_LIST) +do + lspv | grep $VG >> $HDISKFILE # List of Varied ON PVs +done + +twirl & # Gives the user some feedback during long processing times... + +TWIRL_PID=$! + +echo "\nTranslating hdisk(s) into the associated pdisk(s)...Please Wait... \c" + +for DISK in $(cat $HDISKFILE) # Translate hdisk# into pdisk#(s) +do + # Checking for an SSA disk + /usr/sbin/ssaxlate -l $DISK # 2>/dev/null 1>/dev/null + + if (($? == 0)) + then + /usr/sbin/ssaxlate -l $DISK >> $PDISKFILE # Add to pdisk List + fi +done + +kill -9 $TWIRL_PID # Kill the user feedback function... +echo "\b " + +echo "\nTurning $STATE all VARIED-ON system pdisks...Please Wait...\n" + +for PDISK in $(cat $PDISKFILE) +do # Act on each pdisk individually... + echo "Turning $STATE ==> $PDISK" + /usr/sbin/ssaidentify -l $PDISK -${SWITCH} || echo "Turning $STATE $PDISK Failed" +done + +echo "\n\t...TASK COMPLETE...\n" +} + +############################################ + +function list_of_disks +{ +# TURN ON/OFF INDIVDUAL LIGHTS: +# Loop through each of the disks that was passed to this script +# via the positional parameters greater than $1, i.e., $2, $3, $4... +# We first determine if each of the parameters is a pdisk or an hdisk. +# For each hdisk passed to the script we first need to translate +# the hdisk definition into a pdisk definition. This script has +# been set up to accept a combination of hdisks and pdisks. +# +# We will either turn the identifier lights on or off, specified by +# the $SWITCH variable for each pdisk#: +# +# Turn lights on: -y +# Turn lights off: -n +# +# as the $SWITCH value to the "ssaidentify" command. + +echo "\n" + +# The disks passed to this script can be all hdisks, all pdisks +# or a combination of pdisks and hdisks; it just does not matter. +# We translate each hdisk into the associated pdisk(s). + +echo "\nTurning $STATE individual SSA disk lights...\n" + +for PDISK in $PDISKLIST +do + # Is it a real pdisk?? + if [ -c /dev/${PDISK} ] 2>/dev/null + then # Yep - act on it... + + /usr/sbin/ssaidentify -l $PDISK -${SWITCH} + if [ $? -eq 0 ] + then + /usr/bin/ssaxlate -l $PDISK -${SWITCH} + if (($? == 0)) + then + echo "Light on $PDISK is $STATE" + else + echo "Turning $STATE $PDISK Failed" + fi + fi + else + echo "\nERROR: $PDISK is not a defined device on $THISHOST\n" + fi +done + +echo "\n...TASK COMPLETE...\n" +} + +############################################ +############# BEGINNING OF MAIN ############ +############################################ + +# Set a trap... + +# Remember...Cannot trap a "kill -9" !!! + +trap 'cleanup; exit 1' 1 2 3 15 + +############################################## + +# Check for the correct number of arguments (1) + +if (($# == 0)) +then + usage +fi + +############################################## + +# See if the system has any pdisks defined before proceeding + +PCOUNT=$(lsdev -Cc pdisk -s ssar | grep -c pdisk) + +if ((PCOUNT == 0)) +then + echo "\nERROR: This system has no SSA disks defined\n" + echo "\t\t...EXITING...\n" + exit 1 +fi + +############################################## + +# Make sure that the ssaidentify program is +# executable on this system... + +if [ ! -x /usr/sbin/ssaidentify ] +then + echo "\nERROR: /usr/sbin/ssaidentify is NOT an executable" + echo "program on $THISHOST" + echo "\n...EXITING...\n" + exit 1 +fi + +############################################## + +# Make sure that the ssaxlate program is +# executable on this system... + +if [ ! -x /usr/sbin/ssaxlate ] +then + echo "\nERROR: /usr/sbin/ssaxlate is NOT an executable" + echo "program on $THISHOST" + echo "\n...EXITING...\n" + exit 1 +fi + +############################################## +############################################## +# +# Okay, we should have valid data at this point +# Let.s do a light show. +# +############################################## +############################################## + +# Always use the UPPERCASED value for the $STATE, $MODE, +# and $PASSED variables... + +typeset -u MODE +MODE="DEFINED_DISKS" +typeset -u STATE +STATE=UNKNOWN +typeset -u PASSED + +# Use lowercase for the argument list +typeset -l ARGUMENT + +# Grab the system hostname + +THISHOST=$(hostname) + +# Define the hdisk and pdisk FILES + +HDISKFILE="/tmp/disklist.out" +>$HDISKFILE +PDISKFILE="/tmp/pdisklist.identify" +>$PDISKFILE + +# Define the hdisk and pdisk list VARIABLES + +HDISKLIST= +PDISKLIST= + +# Use getopts to parse the command-line arguments + +while getopts ":vV" ARGUMENT 2>/dev/null +do + case $ARGUMENT in + v|V) MODE="VARIED_ON" + ;; + \?) man_page + ;; + esac +done + +############################################## + +# Decide if we are to turn the lights on or off... + +(echo $@ | grep -i -w on >/dev/null) && STATE=ON +(echo $@ | grep -i -w off >/dev/null) && STATE=OFF + +case $STATE in + +ON) + # Turn all of the lights ON... + SWITCH="y" + ;; +OFF) + # Turn all of the lights OFF... + SWITCH="n" + ;; +*) + # Unknown Option... + echo "\nERROR: Please indicate the action to turn lights ON or OFF\n" + usage + exit 1 + ;; +esac + +############################################## +############################################## +########## PLAY WITH THE LIGHTS ############## +############################################## +############################################## + +if (($# == 1)) && [[ $MODE = "DEFINED_DISKS" ]] +then + # This function will turn all lights on/off + + all_defined_pdisks + +elif [[ $MODE = "VARIED_ON" ]] && (($# = 2)) +then + # This function will turn on/off SSA disk lights + # in currently varied-on volume groups only + + all_varied_on_pdisks + +# Now check for hdisk and pdisk arguments + +elif [ $MODE = DEFINED_DISKS ] && (echo $@ | grep disk >/dev/null) \ + && (($# >= 2)) +then + # If we are here we must have a list of hdisks + # and/or pdisks + + # Look for hdisks and pdisks in the command-line arguments + + for DISK in $(echo $@ | grep disk) + do + case $DISK in + hdisk*) HDISKLIST="$HDISKLIST $DISK" + ;; + pdisk*) PDISKLIST="$PDISKLIST $DISK" + ;; + *) : # No-Op - Do nothing + ;; + esac + done + + if [[ ! -z "$HDISKLIST" ]] # Check for hdisks to convert to pdisks + then + + # We have some hdisks that need to be converted to pdisks + # so start converting the hdisks to pdisks + + # Give the user some feedback + + echo "\nConverting hdisks to pdisk definitions" + echo "\n ...Please be patient...\n" + + # Start converting the hdisks to pdisks + + for HDISK in $HDISKLIST + do + PDISK=$(ssaxlate -l $HDISK) + if (($? == 0)) + then + echo "$HDISK translates to ${PDISK}" + else + echo "ERROR: hdisk to pdisk translation FAILED for $HDISK" + fi + + # Build a list of pdisks + # Add pdisk to the pdisk list + + PDISKLIST="$PDISKLIST $PDISK" + done + fi + + if [[ -z "$PDISKLIST" ]] + then + echo "\nERROR: You must specify at least one hdisk or pdisk\n" + man_page + exit 1 + else + # Turn on/off the SSA identification lights + + list_of_disks + fi +fi + +############################################## +# END OF SCRIPT # +############################################## diff --git a/chapter21/function_all_defined_pdisks b/chapter21/function_all_defined_pdisks new file mode 100755 index 0000000..acd74a5 --- /dev/null +++ b/chapter21/function_all_defined_pdisks @@ -0,0 +1,26 @@ +function all_defined_pdisks + +{ + # TURN ON/OFF ALL LIGHTS: + # Loop through each of the system.s pdisks by using the "lsdev" + # command with the "-Cc pdisk" switch while using "awk" to extract + # out the actual pdisk number. We will either + # turn the identifier lights on or off, specified by the + # $SWITCH variable: + # + # Turn lights on: -y + # Turn lights off: -n + # + # as the $SWITCH value to the "ssaidentify" command, as used below... + +echo "\nTurning $STATE ALL of the system.s pdisks...Please Wait...\n" + +for PDISK in $(lsdev -Cc pdisk -s ssar -H | awk .{print $1}. | grep pdisk) +do + echo "Turning $STATE ==> $PDISK" + ssaidentify -l $PDISK -${SWITCH} || echo "Turning $STATE $PDISK Failed" +done +echo "\n...TASK COMPLETE...\n" + +} + diff --git a/chapter21/function_all_varied_on_pdisks b/chapter21/function_all_varied_on_pdisks new file mode 100755 index 0000000..3b94e5b --- /dev/null +++ b/chapter21/function_all_varied_on_pdisks @@ -0,0 +1,46 @@ +function all_varied_on_pdisks +{ +trap 'kill -9 $TWIRL_PID; return 1' 1 2 3 15 + +cat /dev/null > $HDISKFILE + +echo "\nGathering a list of Varied on system SSA disks...Please wait...\c" + +VG_LIST=$(lsvg -o) # Get the list of Varied ON Volume Groups + +for VG in $(echo $VG_LIST) +do + lspv | grep $VG >> $HDISKFILE # List of Varied ON PVs +done + +twirl & # Gives the user some feedback during long processing times... + +TWIRL_PID=$! + +echo "\nTranslating hdisk(s) into the associated pdisk(s)...Please Wait... \c" + +for DISK in $(cat $HDISKFILE) # Translate hdisk# into pdisk#(s) +do + # Checking for an SSA disk + /usr/sbin/ssaxlate -l $DISK # 2>/dev/null 1>/dev/null + + if (($? == 0)) + then + /usr/sbin/ssaxlate -l $DISK >> $PDISKFILE # Add to pdisk List + fi +done + +kill -9 $TWIRL_PID # Kill the user feedback function... +echo "\b " + +echo "\nTurning $STATE all VARIED-ON system pdisks...Please Wait...\n" + +for PDISK in $(cat $PDISKFILE) +do # Act on each pdisk individually... + echo "Turning $STATE ==> $PDISK" + /usr/sbin/ssaidentify -l $PDISK -${SWITCH} || echo "Turning $STATE $PDISK Failed" +done + +echo "\n\t...TASK COMPLETE...\n" +} + diff --git a/chapter21/function_list_of_disks b/chapter21/function_list_of_disks new file mode 100755 index 0000000..d54d397 --- /dev/null +++ b/chapter21/function_list_of_disks @@ -0,0 +1,51 @@ +function list_of_disks +{ +# TURN ON/OFF INDIVDUAL LIGHTS: +# Loop through each of the disks that was passed to this script +# via the positional parameters greater than $1, i.e., $2, $3, $4... +# We first determine if each of the parameters is a pdisk or an hdisk. +# For each hdisk passed to the script we first need to translate +# the hdisk definition into a pdisk definition. This script has +# been set up to accept a combination of hdisks and pdisks. +# +# We will either turn the identifier lights on or off, specified by +# the $SWITCH variable for each pdisk#: +# +# Turn lights on: -y +# Turn lights off: -n +# +# as the $SWITCH value to the "ssaidentify" command. + +echo "\n" + +# The disks passed to this script can be all hdisks, all pdisks +# or a combination of pdisks and hdisks; it just does not matter. +# We translate each hdisk into the associated pdisk(s). + +echo "\nTurning $STATE individual SSA disk lights...\n" + +for PDISK in $PDISKLIST +do + # Is it a real pdisk?? + if [ -c /dev/${PDISK} ] 2>/dev/null + then # Yep - act on it... + + /usr/sbin/ssaidentify -l $PDISK -${SWITCH} + if [ $? -eq 0 ] + then + /usr/bin/ssaxlate -l $PDISK -${SWITCH} + if (($? == 0)) + then + echo "Light on $PDISK is $STATE" + else + echo "Turning $STATE $PDISK Failed" + fi + fi + else + echo "\nERROR: $PDISK is not a defined device on $THISHOST\n" + fi +done + +echo "\n...TASK COMPLETE...\n" +} + diff --git a/chapter21/function_man_page b/chapter21/function_man_page new file mode 100755 index 0000000..401e005 --- /dev/null +++ b/chapter21/function_man_page @@ -0,0 +1,55 @@ +function man_page +{ +MAN_FILE="/tmp/man_file.out" +>$MAN_FILE + +# Text for the man page... + +echo "\n\t\tMAN PAGE FOR SSAidentify.ksh SHELL SCRIPT\n +This script is used to turn on, or off, the system's SSA disk drive +identification lights. You can use this script in the following ways:\n +To turn on/off ALL DEFINED SSA drive identification lights, ALL VARIED-ON SSA +drive identification lights (-v switch), AN INDIVIDUAL SSA drive identification +light or A LIST OF SSA drive identification lights.\n +SSA disk drives can be specified by EITHER the pdisk OR the hdisk, or +a COMBINATION OF BOTH. The script translates all hdisks into the +associated pdisk(s) using the system's /usr/sbin/ssaxlate command and turns +the SSA identification light on/off using the system's /usr/sbin/ssaidentify +command.\n + +This script has four switches that control its' action:\n +-? - Displays this man page.\n +on - Turns the SSA identify light(s) ON.\n +off - Turns the SSA identify light(s) OFF.\n +-v - Specifies to only act on SSA disks which are in currently varied-on +volume groups. The default action is to act on ALL DEFINED SSA disks.\n +NOTE: This switch is ignored for turning on/off individual SSA drive lights, +only valid when turning on/off ALL lights. This option is very helpful in an +HACMP environment since ALL DEFINED, the default action, will turn on/off all +of the SSA drive lights even if the SSA disk is in a volume group which is not +currently varied-on. This can be confusing in an HA cluster.\n +Using this script is very straight forward. The following examples show the +correct use of this script:\n" >> $MAN_FILE +echo "\nUSAGE: SSAidentify.ksh [-v] [on] [off] [pdisk#/hdisk#] [pdisk#/hdisk# list] +\n\nTo Turn ALL Lights Either ON or OFF: +\nUSAGE: SSAidentify.ksh [-v] [on] [off] +\nEXAMPLE: $SCRIPTNAME on +\nWill turn ON ALL of the system's DEFINED SSA identify lights. +This is the default. +EXAMPLE: SSAidentify.ksh -v on +\nWill turn ON ALL of the system's currently VARIED ON +SSA identify lights. OVERRIDES THE DEFAULT ACTION OF ALL DEFINED SSA DISKS +\nTo Turn SPECIFIC LIGHTS Either ON or OFF Using EITHER +the pdisk#(s) AND/OR the hdisk#(s): +\nUSAGE: $SCRIPTNAME [on] [off] pdisk{#1} [hdisk{#2}]... +\nEXAMPLE: $SCRIPTNAME on hdisk36 pdisk44 pdisk47 +\nWill turn ON the lights to all of the associated pdisk#(s) +that hdisk36 translates to and PDISKS pdisk44 and pdisk47. +\nNOTE: Can use all pdisks, all hdisks or BOTH hdisk +and pdisk together if you want...\n\n" >> $MAN_FILE + +more $MAN_FILE + +# End of man_page function +} + diff --git a/chapter21/function_twirl b/chapter21/function_twirl new file mode 100755 index 0000000..c03f89f --- /dev/null +++ b/chapter21/function_twirl @@ -0,0 +1,26 @@ +function twirl +{ +TCOUNT="0" # For each TCOUNT the line twirls one increment + +while : # Loop forever...until you break out of the loop +do + TCOUNT=$(expr ${TCOUNT} + 1) # Increment the TCOUNT + case ${TCOUNT} in + "1") echo '-'"\b\c" + sleep 1 + ;; + "2") echo '\\'"\b\c" + sleep 1 + ;; + "3") echo "|\b\c" + sleep 1 + ;; + "4") echo "/\b\c" + sleep 1 + ;; + *) TCOUNT="0" ;; # Reset the TCOUNT to "0", zero. + esac +done +# End of twirl finction +} + diff --git a/chapter22/function_ping_host b/chapter22/function_ping_host new file mode 100755 index 0000000..53abcaa --- /dev/null +++ b/chapter22/function_ping_host @@ -0,0 +1,40 @@ +function ping_host +{ +# This function pings a single node based on the Unix flavor +# set -x # Uncomment to debug this function +# set -n # Uncomment to check the syntax without any execution + +# Look for exactly one argument, the host to ping + +if (( $# != 1 )) +then + echo "\nERROR: Incorrect number of arguments - $#" + echo " Expecting exactly one augument\n" + echo "\t...EXITING...\n" + exit 1 +fi + +HOST=$1 # Grab the host to ping from ARG1. + +# This next case statement executes the correct ping +# command based on the Unix flavor + +case $UNAME in + +AIX|OpenBSD|Linux) + ping -c${PING_COUNT} $HOST 2>/dev/null + ;; +HP-UX) + ping $HOST $PACKET_SIZE $PING_COUNT 2>/dev/null + ;; +SunOS) + ping -s $HOST $PACKET_SIZE $PING_COUNT 2>/dev/null + ;; +*) + echo "\nERROR: Unsupported Operatoring System - $(uname)" + echo "\n\t...EXITING...\n" + exit 1 + ;; +esac +} + diff --git a/chapter22/function_ping_nodes b/chapter22/function_ping_nodes new file mode 100755 index 0000000..546de7c --- /dev/null +++ b/chapter22/function_ping_nodes @@ -0,0 +1,70 @@ +function ping_nodes +{ +####################################################### +# +# Ping the other systems check +# +# This can be disabled if you do not want every node to be pinging all of the +# other nodes. It is not necessary for all nodes to ping all other nodes. +# Although, you do want more than one node doing the pinging just in case +# the pinging node is down. To activate pinging the "$PINGNODES" variable +# must be set to "TRUE". Any other value will disable pinging from this node. +# + +# set -x # Uncomment to debug this function +# set -n # Uncomment to check command syntax without any execution + +if [ $PINGNODES = "TRUE" ] +then + echo # Add a single line to the output + + # Loop through each node in the $PINGLIST + + for HOSTPINGING in $PINGLIST # Spaces between nodes in the + # list is assumed + do + # Inform the user what is going On + + echo "Pinging --> ${HOSTPINGING}...\c" + + # If the pings received back is equal to "0" then you have a + # problem. + + # Ping $PING_COUNT times, extract the value for the pings + # received back. + + PINGSTAT=$(ping_host $HOSTPINGING | grep transmitted \ + | awk '{print $4}') + + # If the value of $PINGSTAT is NULL, then the node is + # unknown to this host + + if [[ -z "$PINGSTAT" && "$PINGSTAT" = '' ]] + then + echo "Unknown host" + continue + fi + if (( $PINGSTAT == 0 )) + then # Let's do it again to make sure it really is reachable + + echo "Unreachable...Trying one more time...\c" + sleep $INTERVAL + PINGSTAT2=$(ping_host $HOSTPINGING | grep transmitted \ + | awk '{print $4}') + + if [ $PINGSTAT2 -eq "0" ] + then # It REALLY IS unreachable...Notify!! + echo "Unreachable" + echo "Unable to ping $HOSTPINGING from $THISHOST" \ + | tee -a $PING_OUTFILE + else + echo "OK" + fi + else + echo "OK" + fi + + done +fi +} + diff --git a/chapter22/pingnodes.ksh b/chapter22/pingnodes.ksh new file mode 100755 index 0000000..15966f1 --- /dev/null +++ b/chapter22/pingnodes.ksh @@ -0,0 +1,220 @@ +#!/usr/bin/ksh +# +# +# SCRIPT: pingnodes.ksh +# AUTHOR: Randy Michael +# DATE: 02-20-2007 +# +# PURPOSE: This script is used to ping a list of nodes and +# send e-mail notification (or alpha-numeric page) of any unreachable +# nodes. +# +# REV: 1.0.A +# +# REV.LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check command syntax without any execution +# +####################################################### + +# Set a trap and cleanup before a trapped exit... +# REMEMBER: you CANNOT trap "kill -9" + +trap 'echo "\n\nExiting on trapped signal...\n" \ + ;exit 1' 1 2 3 15 + +####################################################### + +# Define and initialize variables here... + +PING_COUNT="3" # The number of times to ping each node +PACKET_SIZE="56" # Packet size of each ping + +typeset -u PINGNODES # Always use the UPPERCASE value for $PINGNODES +PINGNODES="TRUE" # To enable or disable pinging FROM this node - "TRUE" + +typeset -u MAILOUT # Always use the UPPERCASE value for $MAILOUT +MAILOUT="TRUE" # TRUE enables outbound mail notification of events + +UNAME=$(uname) + +PINGFILE="/usr/local/bin/ping.list" # List of nodes to ping + +if [ -s $PINGFILE ] +then + # Ping all nodes in the list that are not commented out and blank + PINGLIST=$(cat $PINGFILE | egrep -v '^#') +else + echo "\nERROR: Missing file - $PINGFILE" + echo "\nList of nodes to ping is unknown...EXITING...\n" + exit 2 +fi + +MAILFILE="/usr/local/bin/mail.list" # List of persons to notify + +if [ -s $MAILFILE ] +then + # Ping all nodes in the list that are not commented out and blank + MAILLIST=$(cat $MAILFILE | egrep -v '^#') +else + echo "\nERROR: Missing file - $MAILFILE" + echo "\nList of persons to notify is unknown...\n" + echo "No one will be notified of unreachable nodes...\n" +fi + +PING_OUTFILE="/tmp/pingfile.out" # File for emailed notification +>$PING_OUTFILE # File for emailed notification + +integer INTERVAL="3" # Number of seconds to sleep between retries + +PINGSTAT= # Number of pings received back from pinging a node +PINGSTAT2= # Number of pings received back on the second try + +THISHOST=`hostname` # The hostname of this machine + +######################################################## +############ DEFINE FUNCTIONS HERE ##################### +######################################################## + +function ping_host +{ +# This function pings a single node based on the Unix flavor +# set -x # Uncomment to debug this function +# set -n # Uncomment to check the syntax without any execution + +# Look for exactly one argument, the host to ping + +if (( $# != 1 )) +then + echo "\nERROR: Incorrect number of arguments - $#" + echo " Expecting exactly one augument\n" + echo "\t...EXITING...\n" + exit 1 +fi + +HOST=$1 # Grab the host to ping from ARG1. + +# This next case statement executes the correct ping +# command based on the Unix flavor + +case $UNAME in + +AIX|OpenBSD|Linux) + ping -c${PING_COUNT} $HOST 2>/dev/null + ;; +HP-UX) + ping $HOST $PACKET_SIZE $PING_COUNT 2>/dev/null + ;; +SunOS) + ping -s $HOST $PACKET_SIZE $PING_COUNT 2>/dev/null + ;; +*) + echo "\nERROR: Unsupported Operatoring System - $(uname)" + echo "\n\t...EXITING...\n" + exit 1 + ;; +esac +} + +####################################################### + +function ping_nodes +{ +####################################################### +# +# Ping the other systems check +# +# This can be disabled if you do not want every node to be pinging all of the +# other nodes. It is not necessary for all nodes to ping all other nodes. +# Although, you do want more than one node doing the pinging just in case +# the pinging node is down. To activate pinging the "$PINGNODES" variable +# must be set to "TRUE". Any other value will disable pinging from this node. +# + +# set -x # Uncomment to debug this function +# set -n # Uncomment to check command syntax without any execution + +if [ $PINGNODES = "TRUE" ] +then + echo # Add a single line to the output + + # Loop through each node in the $PINGLIST + + for HOSTPINGING in $PINGLIST # Spaces between nodes in the + # list is assumed + do + # Inform the user what is going On + + echo "Pinging --> ${HOSTPINGING}...\c" + + # If the pings received back is equal to "0" then you have a + # problem. + + # Ping $PING_COUNT times, extract the value for the pings + # received back. + + PINGSTAT=$(ping_host $HOSTPINGING | grep transmitted \ + | awk '{print $4}') + + # If the value of $PINGSTAT is NULL, then the node is + # unknown to this host + + if [[ -z "$PINGSTAT" && "$PINGSTAT" = '' ]] + then + echo "Unknown host" + continue + fi + if (( $PINGSTAT == 0 )) + then # Let's do it again to make sure it really is reachable + + echo "Unreachable...Trying one more time...\c" + sleep $INTERVAL + PINGSTAT2=$(ping_host $HOSTPINGING | grep transmitted \ + | awk '{print $4}') + + if [ $PINGSTAT2 -eq "0" ] + then # It REALLY IS unreachable...Notify!! + echo "Unreachable" + echo "Unable to ping $HOSTPINGING from $THISHOST" \ + | tee -a $PING_OUTFILE + else + echo "OK" + fi + else + echo "OK" + fi + + done +fi +} + +###################################################### + +function send_notification +{ +if [ -s $PING_OUTFILE -a "$MAILOUT" = "TRUE" ]; +then + case $UNAME in + AIX|HP-UX|Linux|OpenBSD) SENDMAIL="/usr/sbin/sendmail" + ;; + SunOS) SENDMAIL="/usr/lib/sendmail" + ;; + esac + + echo "\nSending email notification" + $SENDMAIL -f randy@$THISHOST $MAILLIST < $PING_OUTFILE +fi +} + +################################################## +############ START of MAIN ####################### +################################################## + + +ping_nodes +send_notification + + +# End of script diff --git a/chapter23/AIXsysconfig.ksh b/chapter23/AIXsysconfig.ksh new file mode 100755 index 0000000..23e3ab9 --- /dev/null +++ b/chapter23/AIXsysconfig.ksh @@ -0,0 +1,526 @@ +#!/usr/bin/ksh +# +# SCRIPT: AIXsysconfig.ksh +# +# AUTHOR: Randy Michael +# REV: 2.1.A +# DATE: 06/14/2007 +# +# PLATFORM: AIX only +# +# PURPOSE: Take a snapshot of the system for later comparision in the +# event of system problems. All data is stored in +# /usr/local/reboot in the file defined to the $SYSINFO_FILE +# variable below. +# +# +# REV LIST: +# 7/11/2007: Changed this script to use a single output file +# that receives data from a series of commands +# within a bunch of functions. +# +# 10/11/2007: Added the following commands to capture +# the AIX technology level and patch set, and +# print the system configuration +# +# oslevel -s # Show TL and patch levels +# +# prtconf # Print the system configuration +# +############ +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to verify command syntax without execution +# +################################################# +######### DEFINE VARIABLES HERE ################# +################################################# + +THISHOST=$(hostname) +DATETIME=$(date +%m%d%y_%H%M%S) +WORKDIR="/usr/local/reboot" +SYSINFO_FILE="${WORKDIR}/sys_snapshot.${THISHOST}.$DATETIME" + +################################################# +############ DEFINE FUNCTIONS HERE ############## +################################################# + +get_host () +{ +# Hostname of this machine + +hostname + +# uname -n # works too +} + +################################################# + +get_OS () +{ +# Operating System - AIX or exit + +uname -s +} + +################################################# + +get_OS_level () +{ +# Query for the operating system release and version level + +oslevel -r + +OSL=$(oslevel -r | cut -c1-2) +if (( OSL >= 53 )) +then + echo "Technology Level: $(oslevel -s)" +fi +} + +################################################# + +get_ML_for_AIX () +{ +# Query the system for the maintenance level patch set + +instfix -i | grep AIX_ML +} + +################################################# + +print_sys_config () +{ +prtconf +} + +################################################# + +get_TZ () +{ +# Get the time zone that the system is operating in. + +cat /etc/environment | grep TZ | awk -F'=' '{print $2}' +} + +################################################# + +get_real_mem () +{ +# Query the system for the total real memory + +echo "$(bootinfo -r)KB" + +# lsattr -El sys0 -a realmem | awk '{print $2}' # Works too +} + +################################################# + +get_arch () +{ +# Query the system for the hardware architecture. Newer +# machines use the -M switch and the older Micro-Channel +# architecture (MCA) machines use the -p option for +# the "uname" command. + +ARCH=$(uname -M) +if [[ -z "$ARCH" && "$ARCH" = '' ]] +then + ARCH=$(uname -p) +fi + +echo "$ARCH" +} + +################################################# + +get_devices () +{ +# Query the system for all configured devices + +lsdev -C +} + +################################################# + +get_long_devdir_listing () +{ +# Long listing of the /dev directory. This shows the +# device major and minor numbers and raw device ownership + +ls -l /dev +} + +################################################# + +get_tape_drives () +{ +# Query the system for all configured tape drives + +lsdev -Cc tape +} + +################################################# + +get_cdrom () +{ +# Query the system for all configured CD-ROM devices + +lsdev -Cc cdrom +} + +################################################# + +get_adapters () +{ +# List all configured adapters in the system + +lsdev -Cc adapter +} + +################################################# + +get_routes () +{ +# Save the network routes defined on the system + +netstat -rn +} + +################################################# + +get_netstats () +{ +# Save the network adapter statistics + +netstat -i +} + +################################################# + +get_fs_stats () +{ +# Save the file system statistics + +df -k +echo "\n" +mount +echo "\n" +lsfs +echo "\n" +} + +################################################# + +get_VGs () +{ +# List all defined Volume Groups + +lsvg | sort -r +} + +################################################# + +get_varied_on_VGs () +{ +# List all varied-on Volume Groups + +lsvg -o | sort -r +} + +################################################# + +get_LV_info () +{ +# List the Logical Volumes in each varied-on Volume Group + +for VG in $(get_varied_on_VGs) +do + lsvg -l $VG +done +} + +################################################# + +get_paging_space () +{ +# List the paging space definitions and usage + +lsps -a +echo "\n" +lsps -s +} + +################################################# + +get_disk_info () +{ +# List of all "hdisk"s (hard drives) on the system + +lspv +} + +################################################# + +get_VG_disk_info () +{ +# List disks by Volume Group assignment + +for VG in $(get_varied_on_VGs) +do + lsvg -p $VG +done +} + +################################################# + +get_HACMP_info () +{ +# If the System is running HACMP then save the +# HACMP configuration + +if [ -x /usr/es/sbin/cluster/utilities/cllsif ] +then + /usr/es/sbin/cluster/utilities/cllsif + echo "\n\n" +fi + +if [ -x /usr/essbin/cluster/utilities/clshowres ] +then + /usr/es/sbin/cluster/utilities/clshowres +fi +} + +################################################# + +get_lparstats () +{ +# Listing of the LPAR configuration + +lparstat -i +} + +################################################# + +get_printer_info () +{ +# Wide listing of all defined printers + +lpstat -W | tail +3 +} + +################################################# + +get_process_info () +{ +# List of all active processes + +ps -ef +} + +################################################# + +get_sna_info () +{ +# If the system is using SNA save the SNA configuration + +sna -d s # Syntax for 2.x SNA +if (( $? != 0 )) +then + lssrc -s sna -l # must be SNA 1.x +fi +} + +################################################# + +get_udp_x25_procs () +{ +# Listing of all "udp" and "x25" processes, if +# any are running + +ps -ef | egrep 'udp|x25' | grep -v grep +} + +################################################# + +get_sys_cfg () +{ +# Short listing of the system configuration + +lscfg +} + +################################################# + +get_long_sys_config () +{ +# Long detailed listing of the system configuration + +lscfg -vp +} + +################################################# + +get_installed_filesets () +{ +# Listing of all installed LPP filesets (system installed) + +lslpp -L +} + +################################################# + +check_for_broken_filesets () +{ +# Check the system for broken filesets + +lppchk -vm3 2>&1 +} + +################################################ + +last_logins () +{ +# List the last 100 system logins + +last | tail -100 +} + +################################################# +############## START OF MAIN ################### +################################################# + +# Check for AIX as the operating system + +if [[ $(get_OS) != 'AIX' ]] +then + echo "\nERROR: Incorrect operating system. This + shell script is written for AIX.\n" + echo "\n\t...EXITING...\n" + exit 1 +fi + +################################################# + +# Define the working directory and create this +# directory if it does not exist. + +if [ ! -d $WORKDIR ] +then + mkdir -p $WORKDIR >/dev/null 2>&1 + if (($? != 0)) + then + echo "\nERROR: Permissions do not allow you to create the + $WORKDIR directory. This script must exit. + Please create the $WORKDIR directory and + execute this script again.\n" + echo "\n\t...EXITING...\n" + exit 2 + fi +fi + +################################################# + +{ # Everything enclosed between this opening bracket and the + # later closing bracket is both displayed on the screen and + # also saved in the log file defined as $SYSINFO_FILE. + + +echo "\n\n[ $(basename $0) - $(date) ]\n" + +echo "Saving system information for $THISHOST..." + +echo "\nSystem:\t\t\t$(get_host)" +echo "Time Zone:\t\t$(get_TZ)" +echo "Real Memory:\t\t$(get_real_mem)" +echo "Machine Type:\t\t$(get_arch)" +echo "Operating System:\t$(get_OS)" +echo "AIX Version Level:\t$(get_OS_level)" +echo "\nCurrent Maintenance/Technology Level:\n \ +$(get_ML_for_AIX)" + +echo "\n#################################################\n" +echo "Print System Configuration\n" +print_sys_config +echo "\n#################################################\n" +echo "Installed and Configured Devices\n" +get_devices +echo "\n#################################################\n" +echo "Long Device Directory Listing - /dev\n" +get_long_devdir_listing +echo "\n#################################################\n" +echo "System Tape Drives\n" +get_tape_drives +echo "\n#################################################\n" +echo "System CD-ROM Drives\n" +get_cdrom +echo "\n#################################################\n" +echo "Defined Adapters in the System\n" +get_adapters +echo "\n#################################################\n" +echo "Network Routes\n" +get_routes +echo "\n#################################################\n" +echo "Network Interface Statictics\n" +get_netstats +echo "\n#################################################\n" +echo "Filesystem Statistics\n" +get_fs_stats +echo "\n#################################################\n" +echo "Defined Volume Groups\n" +get_VGs +echo "\n#################################################\n" +echo "Varied-on Volume Groups\n" +get_varied_on_VGs +echo "\n#################################################\n" +echo "Logical Volume Information by Volume Group\n" +get_LV_info +echo "\n#################################################\n" +echo "Paging Space Information\n" +get_paging_space +echo "\n#################################################\n" +echo "Hard Disks Defined\n" +get_disk_info +echo "\n#################################################\n" +echo "Volume Group Hard Drives\n" +get_VG_disk_info +echo "\n#################################################\n" +echo "HACMP Configuration\n" +get_HACMP_info +echo "\n#################################################\n" +echo "LPAR Statistics for this host\n" +get_lparstats +echo "\n#################################################\n" +echo "Printer Information\n" +get_printer_info +echo "\n#################################################\n" +echo "Active Process List\n" +get_process_info +echo "\n#################################################\n" +echo "SNA Information\n" +get_sna_info +echo "\n#################################################\n" +echo "x25 and udp Processes\n" +get_udp_x25_procs +echo "\n#################################################\n" +echo "System Configuration Overview\n" +get_sys_cfg +echo "\n#################################################\n" +echo "Detailed System Configuration\n" +get_long_sys_config +echo "\n#################################################\n" +echo "System Installed Filesets\n" +get_installed_filesets +echo "\n#################################################\n" +echo "Looking for Broken Filesets\n" +check_for_broken_filesets +echo "\n#################################################\n" +echo "List of the last 100 users to login to $THISHOST\n" +last_logins + +echo "\n\nThis report is save in: $SYSINFO_FILE \n" + +# Send all output to both the screen and the $SYSINFO_FILE +# using a pipe to the "tee -a" command" + +} | tee -a $SYSINFO_FILE diff --git a/chapter23/function_check_for_broken_filesets b/chapter23/function_check_for_broken_filesets new file mode 100755 index 0000000..ec94aef --- /dev/null +++ b/chapter23/function_check_for_broken_filesets @@ -0,0 +1,7 @@ +check_for_broken_filesets () +{ +# Check the system for broken filesets + +lppchk -vm3 2>&1 +} + diff --git a/chapter23/function_get_HACMP_info b/chapter23/function_get_HACMP_info new file mode 100755 index 0000000..1cfd9b3 --- /dev/null +++ b/chapter23/function_get_HACMP_info @@ -0,0 +1,17 @@ +get_HACMP_info () +{ +# If the System is running HACMP then save the +# HACMP configuration + +if [ -x /usr/es/sbin/cluster/utilities/cllsif ] +then + /usr/es/sbin/cluster/utilities/cllsif + echo "\n\n" +fi + +if [ -x /usr/essbin/cluster/utilities/clshowres ] +then + /usr/es/sbin/cluster/utilities/clshowres +fi +} + diff --git a/chapter23/function_get_LV_info b/chapter23/function_get_LV_info new file mode 100755 index 0000000..b184c8e --- /dev/null +++ b/chapter23/function_get_LV_info @@ -0,0 +1,10 @@ +get_LV_info () +{ +# List the Logical Volumes in each varied-on Volume Group + +for VG in $(get_varied_on_VGs) +do + lsvg -l $VG +done +} + diff --git a/chapter23/function_get_ML_for_AIX b/chapter23/function_get_ML_for_AIX new file mode 100755 index 0000000..663db71 --- /dev/null +++ b/chapter23/function_get_ML_for_AIX @@ -0,0 +1,7 @@ +get_ML_for_AIX () +{ +# Query the system for the maintenance level patch set + +instfix -i | grep AIX_ML +} + diff --git a/chapter23/function_get_OS b/chapter23/function_get_OS new file mode 100755 index 0000000..646237b --- /dev/null +++ b/chapter23/function_get_OS @@ -0,0 +1,7 @@ +get_OS () +{ +# Operating System - AIX or exit + +uname -s +} + diff --git a/chapter23/function_get_OS_level b/chapter23/function_get_OS_level new file mode 100755 index 0000000..3e55386 --- /dev/null +++ b/chapter23/function_get_OS_level @@ -0,0 +1,13 @@ +get_OS_level () +{ +# Query for the operating system release and version level + +oslevel -r + +OSL=$(oslevel -r | cut -c1-2) +if (( OSL >= 53 )) +then + echo "Technology Level: $(oslevel -s)" +fi +} + diff --git a/chapter23/function_get_TZ b/chapter23/function_get_TZ new file mode 100755 index 0000000..b2557fb --- /dev/null +++ b/chapter23/function_get_TZ @@ -0,0 +1,7 @@ +get_TZ () +{ +# Get the time zone that the system is operating in. + +cat /etc/environment | grep TZ | awk -F'=' '{print $2}' +} + diff --git a/chapter23/function_get_VG_disk_info b/chapter23/function_get_VG_disk_info new file mode 100755 index 0000000..f0784c2 --- /dev/null +++ b/chapter23/function_get_VG_disk_info @@ -0,0 +1,10 @@ +get_VG_disk_info () +{ +# List disks by Volume Group assignment + +for VG in $(get_varied_on_VGs) +do + lsvg -p $VG +done +} + diff --git a/chapter23/function_get_VGs b/chapter23/function_get_VGs new file mode 100755 index 0000000..d083afa --- /dev/null +++ b/chapter23/function_get_VGs @@ -0,0 +1,7 @@ +get_VGs () +{ +# List all defined Volume Groups + +lsvg | sort -r +} + diff --git a/chapter23/function_get_adapters b/chapter23/function_get_adapters new file mode 100755 index 0000000..8c280b2 --- /dev/null +++ b/chapter23/function_get_adapters @@ -0,0 +1,7 @@ +get_adapters () +{ +# List all configured adapters in the system + +lsdev -Cc adapter +} + diff --git a/chapter23/function_get_arch b/chapter23/function_get_arch new file mode 100755 index 0000000..bf7b0ba --- /dev/null +++ b/chapter23/function_get_arch @@ -0,0 +1,16 @@ +get_arch () +{ +# Query the system for the hardware architecture. Newer +# machines use the -M switch and the older Micro-Channel +# architecture (MCA) machines use the -p option for +# the "uname" command. + +ARCH=$(uname -M) +if [[ -z "$ARCH" && "$ARCH" = '' ]] +then + ARCH=$(uname -p) +fi + +echo "$ARCH" +} + diff --git a/chapter23/function_get_cdrom b/chapter23/function_get_cdrom new file mode 100755 index 0000000..f0c91b3 --- /dev/null +++ b/chapter23/function_get_cdrom @@ -0,0 +1,7 @@ +get_cdrom () +{ +# Query the system for all configured CD-ROM devices + +lsdev -Cc cdrom +} + diff --git a/chapter23/function_get_devices b/chapter23/function_get_devices new file mode 100755 index 0000000..fc2e906 --- /dev/null +++ b/chapter23/function_get_devices @@ -0,0 +1,7 @@ +get_devices () +{ +# Query the system for all configured devices + +lsdev -C +} + diff --git a/chapter23/function_get_disk_info b/chapter23/function_get_disk_info new file mode 100755 index 0000000..832011d --- /dev/null +++ b/chapter23/function_get_disk_info @@ -0,0 +1,7 @@ +get_disk_info () +{ +# List of all "hdisk"s (hard drives) on the system + +lspv +} + diff --git a/chapter23/function_get_fs_stats b/chapter23/function_get_fs_stats new file mode 100755 index 0000000..8dbf304 --- /dev/null +++ b/chapter23/function_get_fs_stats @@ -0,0 +1,12 @@ +get_fs_stats () +{ +# Save the file system statistics + +df -k +echo "\n" +mount +echo "\n" +lsfs +echo "\n" +} + diff --git a/chapter23/function_get_host b/chapter23/function_get_host new file mode 100755 index 0000000..376fc43 --- /dev/null +++ b/chapter23/function_get_host @@ -0,0 +1,9 @@ +get_host () +{ +# Hostname of this machine + +hostname + +# uname -n # works too +} + diff --git a/chapter23/function_get_installed_filesets b/chapter23/function_get_installed_filesets new file mode 100755 index 0000000..1962e51 --- /dev/null +++ b/chapter23/function_get_installed_filesets @@ -0,0 +1,7 @@ +get_installed_filesets () +{ +# Listing of all installed LPP filesets (system installed) + +lslpp -L +} + diff --git a/chapter23/function_get_long_devdir_listing b/chapter23/function_get_long_devdir_listing new file mode 100755 index 0000000..e21600d --- /dev/null +++ b/chapter23/function_get_long_devdir_listing @@ -0,0 +1,8 @@ +get_long_devdir_listing () +{ +# Long listing of the /dev directory. This shows the +# device major and minor numbers and raw device ownership + +ls -l /dev +} + diff --git a/chapter23/function_get_long_sys_config b/chapter23/function_get_long_sys_config new file mode 100755 index 0000000..35503dc --- /dev/null +++ b/chapter23/function_get_long_sys_config @@ -0,0 +1,7 @@ +get_long_sys_config () +{ +# Long detailed listing of the system configuration + +lscfg -vp +} + diff --git a/chapter23/function_get_netstats b/chapter23/function_get_netstats new file mode 100755 index 0000000..e8c5453 --- /dev/null +++ b/chapter23/function_get_netstats @@ -0,0 +1,7 @@ +get_netstats () +{ +# Save the network adapter statistics + +netstat -i +} + diff --git a/chapter23/function_get_paging_space b/chapter23/function_get_paging_space new file mode 100755 index 0000000..f62e09c --- /dev/null +++ b/chapter23/function_get_paging_space @@ -0,0 +1,9 @@ +get_paging_space () +{ +# List the paging space definitions and usage + +lsps -a +echo "\n" +lsps -s +} + diff --git a/chapter23/function_get_printer_info b/chapter23/function_get_printer_info new file mode 100755 index 0000000..bfe3118 --- /dev/null +++ b/chapter23/function_get_printer_info @@ -0,0 +1,7 @@ +get_printer_info () +{ +# Wide listing of all defined printers + +lpstat -W | tail +3 +} + diff --git a/chapter23/function_get_process_info b/chapter23/function_get_process_info new file mode 100755 index 0000000..71383e5 --- /dev/null +++ b/chapter23/function_get_process_info @@ -0,0 +1,7 @@ +get_process_info () +{ +# List of all active processes + +ps -ef +} + diff --git a/chapter23/function_get_real_mem b/chapter23/function_get_real_mem new file mode 100755 index 0000000..24301ec --- /dev/null +++ b/chapter23/function_get_real_mem @@ -0,0 +1,9 @@ +get_real_mem () +{ +# Query the system for the total real memory + +echo "$(bootinfo -r)KB" + +# lsattr -El sys0 -a realmem | awk '{print $2}' # Works too +} + diff --git a/chapter23/function_get_routes b/chapter23/function_get_routes new file mode 100755 index 0000000..83cbb68 --- /dev/null +++ b/chapter23/function_get_routes @@ -0,0 +1,7 @@ +get_routes () +{ +# Save the network routes defined on the system + +netstat -rn +} + diff --git a/chapter23/function_get_sna_info b/chapter23/function_get_sna_info new file mode 100755 index 0000000..cec4bf9 --- /dev/null +++ b/chapter23/function_get_sna_info @@ -0,0 +1,11 @@ +get_sna_info () +{ +# If the system is using SNA save the SNA configuration + +sna -d s # Syntax for 2.x SNA +if (( $? != 0 )) +then + lssrc -s sna -l # must be SNA 1.x +fi +} + diff --git a/chapter23/function_get_sys_cfg b/chapter23/function_get_sys_cfg new file mode 100755 index 0000000..b30b164 --- /dev/null +++ b/chapter23/function_get_sys_cfg @@ -0,0 +1,7 @@ +get_sys_cfg () +{ +# Short listing of the system configuration + +lscfg +} + diff --git a/chapter23/function_get_tape_drives b/chapter23/function_get_tape_drives new file mode 100755 index 0000000..b9ae58a --- /dev/null +++ b/chapter23/function_get_tape_drives @@ -0,0 +1,7 @@ +get_tape_drives () +{ +# Query the system for all configured tape drives + +lsdev -Cc tape +} + diff --git a/chapter23/function_get_udp_x25_procs b/chapter23/function_get_udp_x25_procs new file mode 100755 index 0000000..d9df224 --- /dev/null +++ b/chapter23/function_get_udp_x25_procs @@ -0,0 +1,8 @@ +get_udp_x25_procs () +{ +# Listing of all "udp" and "x25" processes, if +# any are running + +ps -ef | egrep 'udp|x25' | grep -v grep +} + diff --git a/chapter23/function_get_varied_on_VGs b/chapter23/function_get_varied_on_VGs new file mode 100755 index 0000000..a1217fb --- /dev/null +++ b/chapter23/function_get_varied_on_VGs @@ -0,0 +1,7 @@ +get_varied_on_VGs () +{ +# List all varied-on Volume Groups + +lsvg -o | sort -r +} + diff --git a/chapter23/function_last_logins b/chapter23/function_last_logins new file mode 100755 index 0000000..e8f39ea --- /dev/null +++ b/chapter23/function_last_logins @@ -0,0 +1,7 @@ +last_logins () +{ +# List the last 100 system logins + +last | tail -100 +} + diff --git a/chapter23/function_print_sys_config b/chapter23/function_print_sys_config new file mode 100755 index 0000000..b1eb288 --- /dev/null +++ b/chapter23/function_print_sys_config @@ -0,0 +1,5 @@ +print_sys_config () +{ +prtconf +} + diff --git a/chapter24/chpwd_menu.ksh b/chapter24/chpwd_menu.ksh new file mode 100755 index 0000000..769b4e5 --- /dev/null +++ b/chapter24/chpwd_menu.ksh @@ -0,0 +1,143 @@ +#!/usr/bin/ksh +# +# SCRIPT: chpwd_menu.ksh +# AUTHOR: Randy Michael +# DATE: 11/05/2007 +# PLATFORM: AIX +# REV: 1.1.P +# +# PURPOSE: This script was created for the Operations Team +# to change user passwords. This shell script uses +# "sudo" to execute the "pwdadm" command as root. +# Each member of the Operations Team needs to be +# added to the /etc/sudoers file. CAUTION: When +# editing the /etc/sudoers file always use the +# /usr/local/sbin/visudo program editor!!! +# NEVER DIRECTLY EDIT THE sudoers FILE!!!! +# +# REV LIST: +# +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check syntax without any execution +# +####################################################### +# DEFINE FUNCTIONS HERE +####################################################### + +function chg_pwd +{ +USER_NAME="$1" + +echo "\nWhen prompted for a password use YOUR NORMAL PASSWORD" +echo "NOT the new password...\n" + +# The next command turns off the checking of the password history + +/usr/local/bin/sudo /usr/bin/pwdadm -f NOCHECK $USER_NAME +if [ $? -ne 0 ] +then + echo "\nERROR: Turning off password history failed..." + usage + exit 1 +fi + +# The next command changes the user's password + +/usr/local/bin/sudo /usr/bin/pwdadm $USER_NAME +if [ $? -ne 0 ] +then + echo "\nERROR: Changing $USER_NAME password failed..." + usage + exit 1 +fi + +# The next command forces the user to change his or her password +# at the next login. + +/usr/local/bin/sudo /usr/bin/pwdadm -f ADMCHG $USER_NAME + +return 0 +} + +####################################################### +# START OF MAIN +####################################################### + +case $(uname) in +AIX) : # Do nothing + ;; + *) echo "ERROR: This script only works on AIX...EXITING..." + exit 10 + ;; +esac + +OPT=0 # Initialize to zero + +clear # Clear the screen + +while [[ $OPT != 99 ]] # Start a loop +do + +# Draw reverse image bar across the top of the screen +# with the system name. + +clear +tput smso +echo " `hostname` " +tput sgr0 +echo "" + +# Draw menu options. + +echo "\n\n\n\n\n\n\n" + +print "10. Change Password" + +echo "\n\n\n\n\n\n\n\n\n" + +print "99. Exit Menu" + +# Draw reverse image bar across bottom of screen, +# with error message, if any. + +tput smso +echo " $MSG " +tput sgr0 + +# Prompt for menu option. + +read OPT + +# Assume invalid selection was taken. Message is always +# displayed, so blank it out when a valid option is selected. + +MSG=" Invalid option selected " +# Option 10 - Change Password + +if [ $OPT -eq 10 ] +then + echo "\nUsername for password change? \c" + read USERNAME + grep $USERNAME /etc/passwd >/dev/null 2>&1 + if [ $? -eq 0 ] + then + chg_pwd $USERNAME + if [ $? -eq 0 ] + then + MSG="$USERNAME password successfully changed" + else + MSG="ERROR: $USERNAME password change failed" + fi + else + MSG=" ERROR: Invalid username $USERNAME " + fi +fi + +# End of Option 99 Loop + +done + +# Erase menu from screen upon exiting. + +clear diff --git a/chapter25/PQ_all_in_one.ksh b/chapter25/PQ_all_in_one.ksh new file mode 100755 index 0000000..25de5f1 --- /dev/null +++ b/chapter25/PQ_all_in_one.ksh @@ -0,0 +1,337 @@ +#!/bin/ksh +# +# SCRIPT: PQ_all_in_one.ksh +# +# AUTHOR: Randy Michael +# DATE: 08/14/2007 +# REV: 2.1.P +# +# PLATFORM/SYSTEMS: AIX, CUPS, HP-UX, Linux, OpenBSD, and Solaris +# +# PURPOSE: This script is used to enable printing and queuing on +# AIX, CUPS, HP-UX, Linux, OpenBDS, and Solaris +# +# REV LIST: +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check syntax without any execution +# +################################################### +# DEFINE FUNCTIONS HERE +################################################### + +function AIX_classic_printing +{ +for Q in $( enq -AW | tail +3 | grep DOWN | awk '{print $1}') +do + enable $Q + (( $? == 0 )) || echo "\n$Q print queue FAILED to enable.\n" +done +} + +########################################################## + +function AIX_SYSV_printing +{ +LOOP=0 # Loop Counter - To grab three lines at a time + +lpc status all | egrep ':|printing|queueing' | while read LINE +do + # Load three unique lines at a time + case $LINE in + *:) Q=$(echo $LINE | cut -d ':' -f1) + ;; + printing*) + PSTATUS=$(echo $LINE | awk '{print $3}') + ;; + queueing*) + QSTATUS=$(echo $LINE | awk '{print $3}') + ;; + esac + + # Increment the LOOP counter + (( LOOP = LOOP + 1 )) + if ((LOOP == 3)) # Do we have all three lines of data? + then + # Check printing status + case $PSTATUS in + disabled) lpc start $Q >/dev/null + (($? == 0)) && echo "\n$Q printing re-started\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + # Check queuing status + case $QSTATUS in + disabled) lpc enable $Q >/dev/null + (($? == 0)) && echo "\n$Q queueing re-enabled\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + LOOP=0 # Reset the loop counter to zero + fi +done +} + +########################################################## + +function CUPS_printing +{ +LOOP=0 # Loop Counter - To grab three lines at a time + +lpc status all | egrep .:|printing|queuing. | while read LINE +do + # Load three unique lines at a time + case $LINE in + *:) Q=$(echo $LINE | cut -d .:. -f1) + ;; + printing*) + PSTATUS=$(echo $LINE | awk .{print $3}.) + ;; + queuing*) + QSTATUS=$(echo $LINE | awk .{print $3}.) + ;; + esac + + # Increment the LOOP counter + (( LOOP = LOOP + 1 )) + if ((LOOP == 3)) # Do we have all three lines of data? + then + # Check printing status + case $PSTATUS in + disabled) cupsenable $Q >/dev/null + (($? == 0)) && echo -e "\n$Q printing re-started\n" + sleep 1 + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + # Check queuing status + case $QSTATUS in + disabled) accept $Q # >/dev/null + (($? == 0)) && echo -e "\n$Q queueing re-enabled\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + LOOP=0 # Reset the loop counter to zero + fi +done +} + +####################################################### + + +function HP_UX_printing +{ +lpstat | grep Warning: | while read LINE +do + if (echo $LINE | grep 'is down') > /dev/null + then + enable $(echo $LINE | awk '{print $3}') + fi + + if (echo $LINE | grep 'queue is turned off') >/dev/null + then + accept $(echo $LINE | awk '{print $3}') + fi +done +} + +################################################################33 + +function Linux_printing +{ +lpc status | tail +2 | while read pqstat[1] pqstat[2] pqstat[3] junk +do + # First check the status of printing for each printer + case ${pqstat[2]} in + disabled) + # Printing is disable - print status and restart printing + echo "${pqstat[1]} Printing is ${pqstat[2]}" + lpc start ${pqstat[1]} + (($? == 0)) && echo "${pqstat[1]} Printing Restarted" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + # Next check the status of queueing for each printer + case ${pqstat[3]} in + disabled) + echo "${pqstat[1]} Queueing is ${pqstat[3]}" + lpc enable ${pqstat[1]} + (($? == 0)) && echo "${pqstat[1]} Printing Restarted" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac +done +} + +############################################################### + +function OpenBSD_printing +{ +LOOP=0 # Loop Counter - To grab three lines at a time + +lpc status all | egrep ':|printing|queuing' | while read LINE +do + # Load three unique lines at a time + case $LINE in + *:) Q=$(echo $LINE | cut -d ':' -f1) + ;; + printing*) + PSTATUS=$(echo $LINE | awk '{print $3}') + ;; + queuing*) + QSTATUS=$(echo $LINE | awk '{print $3}') + ;; + esac + + # Increment the LOOP counter + (( LOOP = LOOP + 1 )) + if ((LOOP == 3)) # Do we have all three lines of data? + then + # Check queuing status + case $QSTATUS in + disabled) lpc enable $Q >/dev/null + (($? == 0)) && echo "\n$Q queueing re-enabled\n" + sleep 1 + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + # Check printing status + case $PSTATUS in + disabled) lpc up $Q >/dev/null + (($? == 0)) && echo "\n$Q printing re-started\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + LOOP=0 # Reset the loop counter to zero + fi +done +} + +####################################################### + +function Solaris_printing +{ +LOOP=0 # Loop Counter - To grab three lines at a time + +lpc status all | egrep ':|printing|queueing' | while read LINE +do + # Load three unique lines at a time + case $LINE in + *:) Q=$(echo $LINE | cut -d ':' -f1) + ;; + printing*) + PSTATUS=$(echo $LINE | awk '{print $3}') + ;; + queueing*) + QSTATUS=$(echo $LINE | awk '{print $3}') + ;; + esac + + # Increment the LOOP counter + (( LOOP = LOOP + 1 )) + if ((LOOP == 3)) # Do we have all three lines of data? + then + # Check printing status + case $PSTATUS in + disabled) lpc start $Q >/dev/null + (($? == 0)) && echo "\n$Q printing re-started\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + # Check queuing status + case $QSTATUS in + disabled) lpc enable $Q >/dev/null + (($? == 0)) && echo "\n$Q queueing re-enabled\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + LOOP=0 # Reset the loop counter to zero + fi +done +} + +###################################################### +############### BEGINNING OF MAIN #################### +###################################################### + +# Is CUPS Running? If CUPS is running we can just +# run the CUPS standard commands. + +ps auxw | grep -q [c]upsd +if (( $? == 0 )) +then + CUPS_printing + exit $? +fi + +# What OS are we running? + +# To start with we need to know the UNIX flavor. +# This case statement runs the uname command to +# determine the OS name. Different functions are +# used for each OS to restart printing and queuing. + +case $(uname) in + +AIX) # AIX okay...Which printer subsystem? + # Starting with AIX 5L we support System V printing also! + + # Check for an active qdaemon using the SRC lssrc command + + if (ps -ef | grep '/usr/sbin/qdaemon' | grep -v grep) >/dev/null 2>&1 + then + # Standard AIX printer subsystem found + AIX_PSS=CLASSIC + elif (ps -ef | grep '/usr/lib/lp/lpsched' | grep -v grep) + then + # AIX System V printer service is running + AIX_PSS=SYSTEMV + fi + + # Call the correct function for Classic AIX or SysV printing + + case $AIX_PSS in + CLASSIC) # Call the classic AIX printing function + AIX_classic_printing + ;; + SYSTEMV) # Call the AIX SysV printing function + AIX_SYSV_printing + ;; + esac + + ;; +HP-UX) # Call the HP-UX printing function + HP_UX_printing + + ;; +Linux) # Call the Linux printing function + Linux_printing + + ;; +OpenBSD) # Call the OpenBSD printing function + OpenBSD_printing + ;; +SunOS) # Call the Soloris printing function + Solaris_printing + + ;; +*) # Anything else is unsupported. + echo "\nERROR: Unsupported Operating System: $(uname)\n" + echo "\n\t\t...EXITING...\n" + ;; +esac diff --git a/chapter25/enable_AIX_classic.ksh b/chapter25/enable_AIX_classic.ksh new file mode 100755 index 0000000..14a27d9 --- /dev/null +++ b/chapter25/enable_AIX_classic.ksh @@ -0,0 +1,24 @@ +#!/bin/ksh +# +# SCRIPT: enable_AIX_classic.ksh +# +# AUTHOR: Randy Michael +# DATE: 03/14/2007 +# REV: 1.1.P +# +# PLATFORM: AIX Only +# +# PURPOSE: This script is used to enable print queues on AIX systems. +# +# REV LIST: +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check syntax without any execution +# + +for Q in $( enq -AW | tail +3 | grep DOWN | awk '{print $1}') +do + enable $Q + (( $? == 0 )) || echo "\n$Q print queue FAILED to enable.\n" +done + diff --git a/chapter25/function_AIX_SYSV_printing b/chapter25/function_AIX_SYSV_printing new file mode 100755 index 0000000..4bee3d0 --- /dev/null +++ b/chapter25/function_AIX_SYSV_printing @@ -0,0 +1,44 @@ +function AIX_SYSV_printing +{ +LOOP=0 # Loop Counter - To grab three lines at a time + +lpc status all | egrep ':|printing|queueing' | while read LINE +do + # Load three unique lines at a time + case $LINE in + *:) Q=$(echo $LINE | cut -d ':' -f1) + ;; + printing*) + PSTATUS=$(echo $LINE | awk '{print $3}') + ;; + queueing*) + QSTATUS=$(echo $LINE | awk '{print $3}') + ;; + esac + + # Increment the LOOP counter + (( LOOP = LOOP + 1 )) + if ((LOOP == 3)) # Do we have all three lines of data? + then + # Check printing status + case $PSTATUS in + disabled) lpc start $Q >/dev/null + (($? == 0)) && echo "\n$Q printing re-started\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + # Check queuing status + case $QSTATUS in + disabled) lpc enable $Q >/dev/null + (($? == 0)) && echo "\n$Q queueing re-enabled\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + LOOP=0 # Reset the loop counter to zero + fi +done +} + diff --git a/chapter25/function_AIX_classic_printing b/chapter25/function_AIX_classic_printing new file mode 100755 index 0000000..de915be --- /dev/null +++ b/chapter25/function_AIX_classic_printing @@ -0,0 +1,9 @@ +function AIX_classic_printing +{ +for Q in $( enq -AW | tail +3 | grep DOWN | awk '{print $1}') +do + enable $Q + (( $? == 0 )) || echo "\n$Q print queue FAILED to enable.\n" +done +} + diff --git a/chapter25/function_CUPS_printing b/chapter25/function_CUPS_printing new file mode 100755 index 0000000..e4109aa --- /dev/null +++ b/chapter25/function_CUPS_printing @@ -0,0 +1,46 @@ +function CUPS_printing +{ +LOOP=0 # Loop Counter - To grab three lines at a time + +lpc status all | egrep .:|printing|queuing. | while read LINE +do + # Load three unique lines at a time + case $LINE in + *:) Q=$(echo $LINE | cut -d .:. -f1) + ;; + printing*) + PSTATUS=$(echo $LINE | awk .{print $3}.) + ;; + queuing*) + QSTATUS=$(echo $LINE | awk .{print $3}.) + ;; + esac + + # Increment the LOOP counter + (( LOOP = LOOP + 1 )) + if ((LOOP == 3)) # Do we have all three lines of data? + then + # Check printing status + case $PSTATUS in + disabled) cupsenable $Q >/dev/null + (($? == 0)) && echo -e "\n$Q printing re-started\n" + sleep 1 + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + # Check queuing status + case $QSTATUS in + disabled) accept $Q # >/dev/null + (($? == 0)) && echo -e "\n$Q queueing re-enabled\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + LOOP=0 # Reset the loop counter to zero + fi +done +} + diff --git a/chapter25/function_HP_UX_printing b/chapter25/function_HP_UX_printing new file mode 100755 index 0000000..d121e2d --- /dev/null +++ b/chapter25/function_HP_UX_printing @@ -0,0 +1,16 @@ +function HP_UX_printing +{ +lpstat | grep Warning: | while read LINE +do + if (echo $LINE | grep 'is down') > /dev/null + then + enable $(echo $LINE | awk '{print $3}') + fi + + if (echo $LINE | grep 'queue is turned off') >/dev/null + then + accept $(echo $LINE | awk '{print $3}') + fi +done +} + diff --git a/chapter25/function_Linux_printing b/chapter25/function_Linux_printing new file mode 100755 index 0000000..1457c6f --- /dev/null +++ b/chapter25/function_Linux_printing @@ -0,0 +1,27 @@ +function Linux_printing +{ +lpc status | tail +2 | while read pqstat[1] pqstat[2] pqstat[3] junk +do + # First check the status of printing for each printer + case ${pqstat[2]} in + disabled) + # Printing is disable - print status and restart printing + echo "${pqstat[1]} Printing is ${pqstat[2]}" + lpc start ${pqstat[1]} + (($? == 0)) && echo "${pqstat[1]} Printing Restarted" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + # Next check the status of queueing for each printer + case ${pqstat[3]} in + disabled) + echo "${pqstat[1]} Queueing is ${pqstat[3]}" + lpc enable ${pqstat[1]} + (($? == 0)) && echo "${pqstat[1]} Printing Restarted" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac +done +} diff --git a/chapter25/function_OpenBSD_printing b/chapter25/function_OpenBSD_printing new file mode 100755 index 0000000..844d1b7 --- /dev/null +++ b/chapter25/function_OpenBSD_printing @@ -0,0 +1,45 @@ +function OpenBSD_printing +{ +LOOP=0 # Loop Counter - To grab three lines at a time + +lpc status all | egrep ':|printing|queuing' | while read LINE +do + # Load three unique lines at a time + case $LINE in + *:) Q=$(echo $LINE | cut -d ':' -f1) + ;; + printing*) + PSTATUS=$(echo $LINE | awk '{print $3}') + ;; + queuing*) + QSTATUS=$(echo $LINE | awk '{print $3}') + ;; + esac + + # Increment the LOOP counter + (( LOOP = LOOP + 1 )) + if ((LOOP == 3)) # Do we have all three lines of data? + then + # Check queuing status + case $QSTATUS in + disabled) lpc enable $Q >/dev/null + (($? == 0)) && echo "\n$Q queueing re-enabled\n" + sleep 1 + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + # Check printing status + case $PSTATUS in + disabled) lpc up $Q >/dev/null + (($? == 0)) && echo "\n$Q printing re-started\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + LOOP=0 # Reset the loop counter to zero + fi +done +} + diff --git a/chapter25/function_Solaris_printing b/chapter25/function_Solaris_printing new file mode 100755 index 0000000..12e1b84 --- /dev/null +++ b/chapter25/function_Solaris_printing @@ -0,0 +1,44 @@ +function Solaris_printing +{ +LOOP=0 # Loop Counter - To grab three lines at a time + +lpc status all | egrep ':|printing|queueing' | while read LINE +do + # Load three unique lines at a time + case $LINE in + *:) Q=$(echo $LINE | cut -d ':' -f1) + ;; + printing*) + PSTATUS=$(echo $LINE | awk '{print $3}') + ;; + queueing*) + QSTATUS=$(echo $LINE | awk '{print $3}') + ;; + esac + + # Increment the LOOP counter + (( LOOP = LOOP + 1 )) + if ((LOOP == 3)) # Do we have all three lines of data? + then + # Check printing status + case $PSTATUS in + disabled) lpc start $Q >/dev/null + (($? == 0)) && echo "\n$Q printing re-started\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + # Check queuing status + case $QSTATUS in + disabled) lpc enable $Q >/dev/null + (($? == 0)) && echo "\n$Q queueing re-enabled\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + LOOP=0 # Reset the loop counter to zero + fi +done +} + diff --git a/chapter25/print_UP_CUPS.ksh b/chapter25/print_UP_CUPS.ksh new file mode 100755 index 0000000..9b4ce30 --- /dev/null +++ b/chapter25/print_UP_CUPS.ksh @@ -0,0 +1,63 @@ +#!/bin/ksh +# +# SCRIPT: print_UP_CUPS.ksh +# +# AUTHOR: Randy Michael +# DATE: 08/27/2007 +# REV: 2.1.P +# +# PLATFORM: ANY RUNNING CUPS DAEMON +# +# PURPOSE: This script is used to enable printing and queuing separately +# on each print queue for CUPS printing. +# +# REV LIST: +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check syntax without any execution +# +################################################# + +LOOP=0 # Loop Counter - To grab three lines at a time + +lpc status all | egrep ':|printing|queuing' | while read LINE +do + # Load three unique lines at a time + case $LINE in + *:) Q=$(echo $LINE | cut -d ':' -f1) + ;; + printing*) + PSTATUS=$(echo $LINE | awk '{print $3}') + ;; + queuing*) + QSTATUS=$(echo $LINE | awk '{print $3}') + ;; + esac + + # Increment the LOOP counter + (( LOOP = LOOP + 1 )) + if ((LOOP == 3)) # Do we have all three lines of data? + then + + # Check printing status + case $PSTATUS in + disabled) cupsenable $Q >/dev/null + (($? == 0)) && echo -e "\n$Q printing re-started\n" + sleep 1 + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + # Check queuing status + case $QSTATUS in + disabled) accept $Q # >/dev/null + (($? == 0)) && echo -e "\n$Q queueing re-enabled\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + LOOP=0 # Reset the loop counter to zero + fi +done diff --git a/chapter25/print_UP_HP-UX.ksh b/chapter25/print_UP_HP-UX.ksh new file mode 100755 index 0000000..35d5f82 --- /dev/null +++ b/chapter25/print_UP_HP-UX.ksh @@ -0,0 +1,30 @@ +#!/bin/ksh +# +# SCRIPT: print_UP_HP-UX.ksh +# +# AUTHOR: Randy Michael +# DATE: 03/14/2007 +# REV: 1.1.P +# +# PLATFORM: HP-UX Only +# +# PURPOSE: This script is used to enable printing and queuing separately +# on each print queue on an HP-UX system. +# +# REV LIST: +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check syntax without any execution + +lpstat | grep Warning: | while read LINE +do + if (echo $LINE | grep 'is down') > /dev/null + then + enable $(echo $LINE | awk '{print $3}') + fi + + if (echo $LINE | grep 'queue is turned off') >/dev/null + then + accept $(echo $LINE | awk '{print $3}') + fi +done diff --git a/chapter25/print_UP_Linux.ksh b/chapter25/print_UP_Linux.ksh new file mode 100755 index 0000000..3220f5f --- /dev/null +++ b/chapter25/print_UP_Linux.ksh @@ -0,0 +1,56 @@ +#!/bin/ksh +# +# SCRIPT: print_UP_Linux.ksh +# +# AUTHOR: Randy Michael +# DATE: 03/14/2007 +# REV: 1.1.P +# +# PLATFORM: Linux Only +# +# PURPOSE: This script is used to enable printing and queuing separately +# on each print queue on a Linux system. Logging can be +# enabled. +# +# REV LIST: +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check syntax without any execution +# +################################################# +# Initial Variables Here +################################################# + +LOGILE=/usr/local/log/PQlog.log +[ -f $LOGFILE ] || echo /dev/null > $LOGFILE + +################################################# + +lpc status | tail +2 | while read pqstat[1] pqstat[2] pqstat[3] junk +do + # First check the status of printing for each printer + case ${pqstat[2]} in + disabled) + # Printing is disabled - print status and restart printing + echo "${pqstat[1]} Printing is ${pqstat[2]}" \ + | tee -a$LOGFILE + lpc start ${pqstat[1]} | tee -a $LOGFILE + (($? == 0)) && echo "${pqstat[1]} Printing Restarted" \ + | tee -a $LOGFILE + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + # Next check the status of queueing for each printer + case ${pqstat[3]} in + disabled) + echo "${pqstat[1]} Queueing is ${pqstat[3]}" \ + | tee -a $LOGFILE + lpc enable ${pqstat[1]} | tee -a $LOGFILE + (($? == 0)) && echo "${pqstat[1]} Printing Restarted" \ + | tee -a $LOGFILE + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac +done diff --git a/chapter25/print_UP_SUN.ksh b/chapter25/print_UP_SUN.ksh new file mode 100755 index 0000000..e6c67b1 --- /dev/null +++ b/chapter25/print_UP_SUN.ksh @@ -0,0 +1,61 @@ +#!/bin/ksh +# +# SCRIPT: print_UP_Solaris.ksh +# +# AUTHOR: Randy Michael +# DATE: 03/14/2007 +# REV: 1.1.P +# +# PLATFORM: Solaris Only +# +# PURPOSE: This script is used to enable printing and queuing separately +# on each print queue on Solaris systems. +# +# +# REV LIST: +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check syntax without any execution +# +################################################# + +LOOP=0 # Loop Counter - To grab three lines at a time + +lpc status all | egrep ':|printing|queueing' | while read LINE +do + # Load three lines at a time + case $LINE in + *:) Q=$(echo $LINE | cut -d ':' -f1) + ;; + printing*) + PSTATUS=$(echo $LINE | awk '{print $3}') + ;; + queueing*) + QSTATUS=$(echo $LINE | awk '{print $3}') + ;; + esac + + # Increment the LOOP counter + (( LOOP = LOOP + 1 )) + if ((LOOP == 3)) # Do we have all three lines of data? + then + # Check printing status + case $PSTATUS in + disabled) lpc start $Q >/dev/null + (($? == 0)) && echo "\n$Q printing re-started\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + # Check queuing status + case $QSTATUS in + disabled) lpc enable $Q >/dev/null + (($? == 0)) && echo "\n$Q queueing re-enabled\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + LOOP=0 # Reset the loop counter to zero + fi +done diff --git a/chapter25/print_UP_SYSV_AIX.ksh b/chapter25/print_UP_SYSV_AIX.ksh new file mode 100755 index 0000000..f417f74 --- /dev/null +++ b/chapter25/print_UP_SYSV_AIX.ksh @@ -0,0 +1,60 @@ +#!/bin/ksh +# +# SCRIPT: print_UP_SYSV_AIX.ksh +# +# AUTHOR: Randy Michael +# DATE: 03/14/2007 +# REV: 1.1.P +# +# PLATFORM: AIX System V Printing +# +# PURPOSE: This script is used to enable printing and queuing separately +# on each print queue on AIX and Solaris systems. +# +# REV LIST: +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check syntax without any execution +# +################################################# + +LOOP=0 # Loop Counter - To grab three lines at a time + +lpc status all | egrep ':|printing|queueing' | while read LINE +do + # Load three unique lines at a time + case $LINE in + *:) Q=$(echo $LINE | cut -d ':' -f1) + ;; + printing*) + PSTATUS=$(echo $LINE | awk '{print $3}') + ;; + queueing*) + QSTATUS=$(echo $LINE | awk '{print $3}') + ;; + esac + + # Increment the LOOP counter + (( LOOP = LOOP + 1 )) + if ((LOOP == 3)) # Do we have all three lines of data? + then + # Check printing status + case $PSTATUS in + disabled) lpc start $Q >/dev/null + (($? == 0)) && echo "\n$Q printing re-started\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + + # Check queuing status + case $QSTATUS in + disabled) lpc enable $Q >/dev/null + (($? == 0)) && echo "\n$Q queueing re-enabled\n" + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + LOOP=0 # Reset the loop counter to zero + fi +done diff --git a/chapter25/printing_only_UP_Linux.ksh b/chapter25/printing_only_UP_Linux.ksh new file mode 100755 index 0000000..90757ff --- /dev/null +++ b/chapter25/printing_only_UP_Linux.ksh @@ -0,0 +1,44 @@ +#!/bin/ksh +# +# SCRIPT: printing_only_UP_Linux.ksh +# +# AUTHOR: Randy Michael +# DATE: 03/14/2007 +# REV: 1.1.P +# +# PLATFORM: Linux Only +# +# PURPOSE: This script is used to enable printing on each printer +# on a Linux system. Logging is enabled. +# +# REV LIST: +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check syntax without any execution +# +################################################# +# Initial Variables Here +################################################# + +LOGILE=/usr/local/log/PQlog.log +[ -f $LOGFILE ] || echo /dev/null > $LOGFILE + +################################################# + +lpc status | tail +2 | while read pqstat[1] pqstat[2] pqstat[3] junk +do + # Check the status of printing for each printer + case ${pqstat[2]} in + disabled) + # Printing is disable - print status and restart printing + echo "${pqstat[1]} Printing is ${pqstat[2]}" \ + | tee -a$LOGFILE + lpc start ${pqstat[1]} | tee -a $LOGFILE + (($? == 0)) && echo "${pqstat[1]} Printing Restarted" \ + | tee -a $LOGFILE + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac + done + diff --git a/chapter25/queuing_only_UP_Linux.ksh b/chapter25/queuing_only_UP_Linux.ksh new file mode 100755 index 0000000..061547b --- /dev/null +++ b/chapter25/queuing_only_UP_Linux.ksh @@ -0,0 +1,43 @@ +#!/bin/ksh +# +# SCRIPT: queuing_only_UP_Linux.ksh +# +# AUTHOR: Randy Michael +# DATE: 03/14/2007 +# REV: 1.1.P +# +# PLATFORM: Linux Only +# +# PURPOSE: This script is used to enable printing and queuing separately +# on each print queue on a Linux system. Logging can be enabled. +# +# REV LIST: +# +# set -x # Uncomment to debug this script +# set -n # Uncomment to check syntax without any execution +# +################################################# +# Initial Variables Here +################################################# + +LOGILE=/usr/local/log/PQlog.log +[ -f $LOGFILE ] || echo /dev/null > $LOGFILE + +################################################# + +lpc status | tail +2 | while read pqstat[1] pqstat[2] pqstat[3] junk +do + # check the status of queueing for each printer + case ${pqstat[3]} in + disabled) + echo "${pqstat[1]} Queueing is ${pqstat[3]}" \ + | tee -a $LOGFILE + lpc enable ${pqstat[1]} | tee -a $LOGFILE + (($? == 0)) && echo "${pqstat[1]} Printing Restarted" \ + | tee -a $LOGFILE + ;; + enabled|*) : # No-Op - Do Nothing + ;; + esac +done + diff --git a/chapter26/chk_passwd_gid_0.bash b/chapter26/chk_passwd_gid_0.bash new file mode 100755 index 0000000..5c518f2 --- /dev/null +++ b/chapter26/chk_passwd_gid_0.bash @@ -0,0 +1,33 @@ +#!/bin/bash +# +# SCRIPT: chk_passwd_gid_0.bash +# +# PURPOSE: This script searches the /etc/passwd +# for all non-root users who are a member of +# the system/root group, GID=0 +# +########################################### +# DECLARE FILES AND VARIABLES HERE +########################################### + +case $(uname) in +SunOS) alias awk=nawk + ;; +esac + +########################################### +# BEGINNING OF MAIN +########################################### + +awk -F ':' '{print $1, $3}' /etc/passwd | while read U G +do + # If the user is root skip the test + if [ $U != 'root' ] + then + # Test for GID=0 + if $(id $U | grep -q 'gid=0' ) + then + echo "WARNING: $U is a member of the root/system group" + fi + fi +done diff --git a/chapter26/search_group_id.bash b/chapter26/search_group_id.bash new file mode 100755 index 0000000..bf7ee29 --- /dev/null +++ b/chapter26/search_group_id.bash @@ -0,0 +1,28 @@ +#!/bin/bash +# +# SCRIPT: search_group_id.Bash +# AUTHOR: Randy Michael +# DATE: 14/4/2007 +# REV: 1.0 +# PURPOSE: This script is used to list the group(s) associated +# with each user defined in the /etc/passwd file. +# +# +########################################### +# DECLARE FILES AND VARIABLES HERE +########################################### + +case $(uname) in +SunOS) alias awk=nawk + ;; +esac + +######################################### +# BEGINNING OF MAIN +######################################### + +cat /etc/passwd | awk -F : '{print $1}' | while read ID +do + echo -e "${ID}: \c" + id -Gn $ID +done diff --git a/chapter27/dirvish_ctrl b/chapter27/dirvish_ctrl new file mode 100755 index 0000000..bddafb8 --- /dev/null +++ b/chapter27/dirvish_ctrl @@ -0,0 +1,1140 @@ +#!/bin/bash +# +# SCRIPT: dirvish_ctrl +# AUTHOR: Randy Michael +# DATE: 5/14/2007 +# REV: 1.0 +# PLATFORM: Most any UNIX platform +# +# PURPOSE: This script is used to interface with the Dirvish +# backup utilities. +# +# set -x # Uncomment to debug this script +# +# set -n # Uncomment to check the script's syntax +# # without any execution. Do not forget to +# # recomment this line! +# +######################################## +# REVISION LIST: +######################################## +# +# IF YOU MODIFY THIS SCRIPT DOCUMENT THE CHANGE(S)!!! +# +######################################## +# +# Revised by: +# Revision Date: +# Revision: +# +# +######################################## +# +# Revised by: +# Revision Date: +# Revision: +# +######################################## +# DEFINE FILES AND VARIABLES HERE +######################################## + +RESTORE_AREA=/dirvish_restores +DIRVISH_SERVER=$(hostname) +DEFAULT_CONF=/tmp/dirvish_build_conf.out +LOGFILE=${DIR}/$(basename $0).log +echo -e "\n\t\tDirvish Log Created: $(date) \n" >$LOGFILE +DONE=0 +PATH=$PATH:/usr/sbin:/usr/local/sbin:. + +######################################## +# DEFINE FUNCTIONS HERE +######################################## + +display_main_menu () +{ +# This function displays the main menu + +clear # Clear the screen + +# Display the menu header and menu + +echo -e "\n\n\tWELCOME TO DIRVISH BACKUP COMMAND-CENTER + + +\t1) Dirvish RunAll Backups + +\t2) Dirvish Run Backup + +\t3) Dirvish Locate/Restore Image + +\t4) Dirvish Expire/Delete Backup(s) + +\t5) Dirvish Add a New Backup + +\t6) Dirvish Remove a Backup + +\t7) Dirvish Manage Backup Banks + +\t8) EXIT + +" +echo -e "\tSelect an Option: \c" +} + +######################################## + +read_input () +{ +# This function is used to save user input + +LINE= # Initialize LINE to NULL + +while true +do + read "ENTRY" + if [[ $ENTRY = 99 ]] + then + echo "$LINE" + break + fi + LINE="$LINE +$ENTRY" +done +} + +######################################## + +parse_conf () +{ +# This function parses through the Dirvish Master +# Configuration File, specified by $M_CONFIG, to +# gather a list of all of the defined Dirvish banks. + +# set -x # Uncomment to debug this function + +# Initial local variables + +BANK_LIST= +START=0 + +# Loop through the $M_CONFIG file until we find +# the 'bank:' stanza, then add each bank to a list +# until we reach another stanza, 'stanza:' +# Loop through the $M_CONFIG file until we find +# the 'bank:' stanza, then add each bank to a list +# until we reach another stanza, 'stanza:' + +while read DATA +do + [[ $DATA = 'bank:' ]] && START=1 && continue + if (( START == 1 )) + then + if $(echo "$DATA" | grep -q ':') + then + break + else + if [[ -z "$BANK_LIST" ]] + then + BANK_LIST="$DATA" + else + BANK_LIST="$BANK_LIST $DATA" + fi + fi + fi +done < $M_CONFIG # Feed the while loop from the bottom + +echo "$BANK_LIST" # Return the list +} + +######################################## + +build_default_conf () +{ +# Define files and variables here + +HN="$1" # Hostname of server to add +BANK="$2" # Bank to use for backup +DAYS="$3" # Days before backups expire +TREE="$4" # Directory tree to back up +IGNORE_LIST="$5" # Files and directories to ignore +OUTFILE=$DEFAULT_CONF # Name of the output file + +# All of the following output is used to build the +# new default.conf file for this backup +{ +echo "client: $HN" +echo "bank: $BANK" +echo "vault: $HN" +echo "server: $DIRVISH_SERVER" +echo "expire: $DAYS day" +echo "index: gzip" +echo "log: gzip" +echo "image: ${HN}-%y%m%d%H%M%S" +echo "tree: $TREE" +echo -e "\nexclude:" +echo "$IGNORE_LIST" | sed /^$/d | while read Q +do + echo -e "\t$Q" +done +} >$OUTFILE +} + +######################################## + +run_all () +{ +# This function runs all of the backups defined in +# the Dirvish master configuration file stanza "Runall:" + +clear # Clear the screen +echo -e "\n\n" + +# Execute all master.conf defined backups + +dirvish-runall + +if (( $? == 0 )) +then + echo -e "\n\tBackups Complete...Press Enter to Continue...\c" +else + echo -e "\n\tDirvish Backup ERROR...Press Enter to Continue...\c" +fi +read KEY +} + +######################################## + +run_backup () +{ +# This function runs one particular backup + +# set -x # Uncomment to debug this function + +clear # Clear the screen + +# Display the screen heading and query the user + +echo -e "\n\n\t\tRUN A PARTICULAR BACKUP\n" +echo -e "\tEnter a Hostname to Backup: \c" +read HTBU # Host to Backup + +echo "Searching for default.conf in ${HTBU}'s Vault" + +BANK_LIST=$(parse_conf) + +for P in $BANK_LIST +do + # Find the Default Config file (default.conf) for $HTBU + + DCF=$(find ${P}/${HTBU} -type f -name default.conf) + if [[ ! -z $DCF ]] + then + echo -e "\nFound Configuration File...Starting Backup..." + dirvish --vault $HTBU + RC=$? + echo -e "\nDirvish Exit Code: $RC" + echo -e "\nBackup Complete..." + echo -e "\nPress Enter to Continue...\c" + read KEY + break + else + echo -e "\nERROR: Could not Locate the Configuration File for $HTBU" + echo -e "\n...You Need to Configure $HTBU for Dirvish Backup First" + echo -e "\nPress Enter to Continue...\c" + read KEY + fi +done + +if [[ -z "$DCF" ]] +then + echo -e "\nERROR: Could not Locate the Configuration File for $HTBU" + echo -e "\n...You Need to Configure $HTBU for Dirvish Backup First" + echo -e "\nPress Enter to Continue...\c" + read KEY +fi +} + +######################################## + +chk_create_dir () +{ +# This function is used to add a new Dirvish "bank" to +# the Dirvish backup server, which should be this machine + +D=$1 # Directory name to add + +if [ ! -d $D ] # Check if the directory already exists +then + echo -e "\n$D Directory Does Not Exist...Create $D Directory? (y/n): \c" + read ANS + case $ANS in + y|Y) echo -e "\nCreating $D Directory..." + mkdir -p $D + if (( $? == 0 )) + then + echo -e "\n$D Directory Created" + else + echo -e "\n$D Directory Creation Failed...See Administrator..." + fi + ;; + n|N) echo -e "\nSkipping Creating the $D Directory" + echo -e "\nWARNING: Until the $D Directory is Created Dirvish Cannot use this Bank\n" + ;; + *) echo -e "\nInvalid Input..." + ;; + esac +fi +} + +######################################## + +locate_restore_image () +{ +# set -x # Uncomment to debug this function + +clear # Clear the screen + +# Display the screen heading, query the user, display the +# files matching the search, and ask if the user wants to +# restore the files. If a restore is requested we ensure +# there is a directory for the target server in the $RESTORE_AREA, +# if not create it, and copy the files into this subdirectory in +# the $RESTORE_AREA. + +echo -e "\n\n\t\tLOCATE/RESTORE A BACKUP IMAGE\n" +echo -e "Enter the Hostname where the File/Directory Resides: \c" +read H +echo -e "\nEnter as Much of the Directory path and File Name as You Know:\n" +read "DP" +if [ "$DP" = '/' ] +then + echo -e "\nERROR: This Program is Not Intended for Full System Restores...\n" + sleep 2 + return +fi +echo -e "\nSearching for Archive Images..." + +# Search and display all matching files one page at a time + +dirvish-locate $H ${DP} | more + +QUIT=0 +until (( QUIT == 1 )) +do + echo -e "\nDid You Find What You Want? (y/n): \c" + read ANS1 + case $ANS1 in + y|Y) echo -e "\nDo You Want to Perform a Restore? (y/n): \c" + read ANS2 + while true + do + case $ANS2 in + y|Y) QUIT=1 + break + ;; + n|N) echo -e "\nPress Enter to Continue...\c" + read KEY + return 0 + ;; + *) echo -e "\nInvalid Input..." + ;; + esac + done + ;; + n|N) return 0 + ;; + *) echo -e "\nInvalid Input..." + ;; + esac +done + +echo -e "\nEnter the FULL PATHNAME of the File/Directory You Want to Restore:\n" +read TARGET + +if [ -z "$TARGET" ] +then + echo -e "\nInvalid Entry..." + sleep 2 + return +fi + +if [[ $(echo "$TARGET" | cut -c1) != '/' ]] +then + echo -e "\nERROR: $TARGET is Not a Valid Full Pathname...\n" + sleep 2 + break +fi + +echo -e "\nSearching Images..." + +dirvish-locate "$H" "${TARGET}" | more + +echo -e "\nEnter the Image to Restore From: \c" +read IMAGE_NAME +echo + +# Check to ensure that the defined $RESTORE_AREA +# directory has been created on the system. + +if [[ ! -d $RESTORE_AREA ]] +then + mkdir -p $RESTORE_AREA +fi + +# Ensure that a subdirectory exists for this host + +if [[ ! -d ${RESTORE_AREA}/${H} ]] +then + mkdir -p ${RESTORE_AREA}/${H} +fi + +BANK_LIST=$(parse_conf) + +# This is where we restore the data + +for BANK in $BANK_LIST +do + if [ -d "$TARGET" ] + then + # If $TARGET is a directory we only want the last + # part of the path, not the entire path. + + TARGET=$(basename "$TARGET") + + find ${BANK}/${H}/${IMAGE_NAME}/tree -name "${TARGET}*" -print \ + -exec cp -rp {} "${RESTORE_AREA}/${H}" \; 2>/dev/null + + if (( $? == 0 )) + then + echo -e "\nFiles Restored to the Following Restore Area:" + echo -e "\n${RESTORE_AREA}/${H}/${TARGET}\n\n" + break + else + echo -e "\nRestore Failed...See Administrator...\n" + break + fi + else + # We are working with a file + + D_NAME=$(dirname "$TARGET" | cut -d '/' -f3-) + F_NAME=$(basename "$TARGET") + echo "DIRNAME is $D_NAME" + + # Find and copy the file(s) to the $RESTORE_AREA + + find ${BANK}/${H}/${IMAGE_NAME}/tree/*${D_NAME} -name "${F_NAME}" \ + -print -exec cp -rp {} "${RESTORE_AREA}/${H}" \; 2>/dev/null + + if (( $? == 0 )) + then + echo -e "\nFiles Restored to the Following Restore Area:" + echo -e "\n${RESTORE_AREA}/${H}/${F_NAME}\n\n" + break + else + echo -e "\nRestore Failed...See Administrator...\n" + break + fi + fi +done + +echo -e "\nPress Enter to Continue...\c" +read KEY +} + +######################################## + +expire_backup () +{ +# This function is used to expire backups + +# set -x # Uncomment to debug this function + +clear # Clear the screen + +# Display the screen header and menu + +echo -e " + \n\n\t\tDIRVISH EXPIRE BACKUP(S)\n + \n\t1) Delete Expired Backups for ALL Hosts + \n\t2) Delete Expired Backups for One Host + \n\t3) Expire One Backup for One Host + \n\t4) Expire All Backups for One Host + \n\t5) Previous Menu + \n\n\tSelect an Option: \c" + +read ANS +case $ANS in +1) echo -e "\n\tDeleting Expired Backups for ALL Hosts..." + dirvish-expire --tree + echo -e "\n\tTasks Complete..." + echo -e "\n\tPress Enter to Continue...\c" + read KEY + ;; +2) echo -e "\n\tEnter the Hostname to Delete Expired Backups: \c" + read H + dirvish-expire --tree --vault $H | more + echo -e "\n\tTasks Complete..." + echo -e "\n\tPress Enter to Continue...\c" + read KEY + ;; +3) EXPIRE_STAMP="$(date "+%Y-%m-%d %H:%M:%S")" + echo -e "\n\tEnter the Hostname for the Backup Image: \c" + read H + + BANK_LIST=$(parse_conf) + MYPWD=$(pwd) + + for B in $BANK_LIST + do + if [ -d ${B}/${H} ] + then + cd ${B}/${H} + IMAGE_LIST=$(find . -type d -name "*-[0-9][0-9]*" | cut -c3-) + fi + done + + echo -e "\nSelect an Image to Expire from the Following List:\n" + echo -e "\n$IMAGE_LIST" | more + echo -e "\n\nEnter the Image to Expire: \c" + read IMAGE_NAME + + cd $MYPWD + if [[ -r ${B}/${H}/${IMAGE_NAME}/summary ]] + then + # This is where we expire a backup that is not currently + # expired. The 'summary' file contains the expire datestamp + + echo -e "\n\tExpiring Image: $IMAGE_NAME" + + sed s/"$(grep Expire ${B}/${H}/${IMAGE_NAME}/summary)"/"$(grep Expire ${B}/${H}/${IMAGE_NAME}/summary | cut -f 1-4 -d ' ') $EXPIRE_STAMP"/g "${B}/${H}/${IMAGE_NAME}/summary" > "${B}/${H}/${IMAGE_NAME}/summary2" + + mv "${B}/${H}/${IMAGE_NAME}/summary2" "${B}/${H}/${IMAGE_NAME}/summary" + fi + + # We expired the backups; now we ask the user if we should delete + # the expired images + + STOP=0 + until (( STOP == 1 )) + do + echo -e "\n\tTasks Complete...Do You Want to Delete This Expired Image? (y/n): \c" + read ANS + case $ANS in + y|Y) echo -e "\n\tDeleting Expired Image: $IMAGE_NAME" + + # This next command deletes all expired images for $H + dirvish-expire --vault $H + + RC=$? + echo -e "\n\tDirvish-expire Completed with Return Code: $RC" + echo -e "\n\tTasks Complete..." + echo -e "\n\tPress Enter to Continue...\c" + read KEY + STOP=1 + ;; + n|N) STOP=1 + continue + ;; + *) echo -e "\n\tInvalid Entry..." + ;; + esac + done + ;; +4) EXPIRE_STAMP=$(date "+%Y-%m-%d %H:%M:%S") + clear + echo -e "\nEnter the Hostname for the Backup Image: \c" + read H + + BANK_LIST=$(parse_conf) + MYPWD=$(pwd) + + for P in $BANK_LIST + do + if [ -d ${P}/${H} ] + then + cd ${P}/${H} + IMAGE_LIST=$(find . -type d -name "*-[0-9][0-9]*" | cut -c3-) + B=$P + fi + done + + echo -e "\nSetting to Following Images to Expire:\n\n" + echo "$IMAGE_LIST" | more + cd $MYPWD + echo -e "\nPress Enter to Continue...\c" + + echo "$IMAGE_LIST" | while read IMAGE_NAME + do + if [ -f ${B}/${H}/${IMAGE_NAME}/summary ] + then + echo "Expiring $IMAGE_NAME" + sed s/"$(grep Expire ${B}/${H}/${IMAGE_NAME}/summary)"/"$(grep Expire ${B}/${H}/${IMAGE_NAME}/summary | cut -f 1-4 -d ' ') $EXPIRE_STAMP"/g "${B}/${H}/${IMAGE_NAME}/summary" > "${B}/${H}/${IMAGE_NAME}/summary2" + + if (( $? == 0 )) + then + mv ${B}/${H}/${IMAGE_NAME}/summary2 ${B}/${H}/${IMAGE_NAME}/summary + fi + fi + done + + STOP=0 + until (( STOP == 1 )) + do + echo -e "\nTasks Complete...Do You Want to Delete Expired Images? (y/n): \c" + read ANS + case $ANS in + y|Y) echo -e "\nDeleting Expired Images...\n" + dirvish-expire --vault $H + RC=$? + echo -e "\nDirvish-expire Completed with Return Code: $RC" + echo -e "\nTasks Complete..." + echo -e "\nPress Enter to Continue...\c" + read KEY + STOP=1 + ;; + n|N) STOP=1 + continue + ;; + *) echo -e "\n\tInvalid Entry..." + ;; + esac + done + ;; +5) : + ;; +*) echo -e "\nInvalid Entry" + ;; +esac +} + +######################################## + +add_backup () +{ +# This function is used to define a new backup to Dirvish + +# set -x # Uncomment to debug this function + +# Get a list of available banks + +BANK_LIST=$(parse_conf) + +ESCAPE=0 +until (( ESCAPE == 1 )) +do + clear + echo -e " + \n\n\t\t DIRVISH ADD A BACKUP + \n\tSelect Each Option to Add a New Backup + \n\t1) Enter Hostname + \n\t2) Select a Bank for this Backup + \n\t3) Enter Directory Tree to Back up + \n\t4) Enter Directory Trees to Ignore + \n\t5) Enter Days to Retain Each Backup + \n\t6) Add this New Dirvish Backup Definition + \n\t7) Create the Initial Dirvish Backup + \n\t8) Main Menu + \n\n\tSelect Each Option to Add a New Backup + \n\n\tEnter Option: \c" + + read OPTION # Read in the user response + + # Perform the desired operation + case $OPTION in + 1) echo -e "\n\tEnter the Hostname for this Backup: \c" + read HN + for B in $BANK_LIST + do + P=$(find $B -type d -name $HN) + if [ ! -z $P ] + then + echo -e "\n\tWARNING: Vault $HN Already Exists in the Following Bank:" + echo -e "\n\t$P" + echo -e "\n\tDo you want to create a new default.conf file for $HN? (y/n): \c" + ANS=n # Set the default answer to 'n', No. + read ANS + case $ANS in + y|Y) continue + ;; + n|N) return + ;; + esac + fi + done + ;; + 2) FINISH=0 + until (( FINISH == 1 )) + do + clear + BANK= + echo -e "\n\n\t\tSELECT A BANK TO STORE THIS BACKUP\n" + echo -e "\nAvailable Banks:\n" + for B in $BANK_LIST + do + echo -e "\t$B" + done + echo -e "\nPlease Enter a Bank: \c:" + read BANK + if $(echo $BANK_LIST | grep -q ${BANK}) + then + echo "$BANK selected for $HN" + FINISH=1 + else + echo -e "\nERROR: $BANK is not a Valid Bank" + sleep 2 + continue + fi + done + ;; + 3) echo -e "\n\tEnter the Directoy Tree to Backup: \c" + read TREE + continue + ;; + 4) clear + echo -e "\n\n\tDIRECTORY TREE(S) and FILES TO IGNORE\n" + echo -e "\nThe Specified Directory Tree Contains the Following Files/Directories:\n\n" + if [ $HN = $(hostname) ] + then + ls $TREE | more + else + ssh $HN ls $TREE | more + fi + echo -e "\nDo you want to Exclude any Files or Directories from the Backups? (y/n): \c" + read ANS3 + case $ANS3 in + y|Y) echo -e "\n\tEnter Directories and Files to Ignore One per Line\n" + echo -e "Enter 99 when finished\n" + IGNORE_LIST=$(read_input) + continue + ;; + n|N) IGNORE_LIST=$(echo) + continue + ;; + *) echo -e "\nInvalid Entry..." + ;; + esac + ;; + 5) echo -e "\n\tDays before each Backup Expires: \c" + read DAYS + continue + ;; + 6) clear + echo -e "\n\n\tADD NEW BACKUP TO DIRVISH" + if [[ -r "${BANK}/${HN}/dirvish/default.conf" ]] + then + echo -e "\nWARNING: default.conf File Exists...Rebuild default.conf? (y/n): \c" + read ANS + case $ANS in + y|Y) echo -e "\n\nCreating default.conf Dirvish Configuration File for $HN" + build_default_conf $HN "$BANK" $DAYS "$TREE" "$IGNORE_LIST" + echo -e "\nCopying file to: ${BANK}/${HN}/dirvish/default.conf" + mkdir -p ${BANK}/${HN}/dirvish + cp -f $DEFAULT_CONF ${BANK}/${HN}/dirvish/default.conf + echo -e "\n\n...Press ENTER to Continue...\c" + read ANS + ;; + *) break + ;; + esac + else + echo -e "\n\nCreating default.conf Dirvish Configuration File for $HN" + build_default_conf $HN "$BANK" $DAYS "$TREE" "$IGNORE_LIST" + echo -e "\nCopying file to: ${BANK}/${HN}/dirvish/default.conf" + mkdir -p ${BANK}/${HN}/dirvish + cp -f $DEFAULT_CONF ${BANK}/${HN}/dirvish/default.conf + echo -e "\n...Press ENTER to Continue...\c" + read ANS + fi + ;; + 7) clear + echo -e "\n\n\tCREATE INITIAL DIRVISH BACKUP" + echo -e "\n" + if [[ ! -d "${BANK}/${HN}" ]] + then + echo -e "\nCreating Vault $HN for this Backup" + mkdir -p "${BANK}/${HN}" + mkdir -p "${BANK}/${HN}/dirvish" + else + echo -e "\nBackup Vault for $HN Exists...Continuing..." + fi + if [[ ! -r "${BANK}/${HN}/dirvish/default.conf" ]] + then + # Now we have enough information to build the + # new default.conf file + + echo -e "Creating default.conf Dirvish Configuration File for $HN" + + build_default_conf $HN "$BANK" $DAYS "$TREE" "$IGNORE_LIST" + + echo -e "\nCopying file to: ${BANK}/${HN}/dirvish/default.conf" + cp -f $DEFAULT_CONF ${BANK}/${HN}/dirvish/default.conf + + # Now we need to run an initial backup + echo -e "\nRun an Initial Backup Now? (y/n): \c" + read BACK_NOW + case $BACK_NOW in + y|Y) echo -e "\nExecuting Initial Dirvish Backup for $HN..." + dirvish --vault $HN --init + RC=$? + echo -e "\nInitial Backup for $HN Completed with a Return Code: $RC" + echo -e "\nPress Enter to Continue...\c" + read KEY + ;; + n|N) echo -e "\nInitial Backup Skipped..." + echo -e "\nDo Not Forget to Run an Initial Dirvish Backup!" + echo -e "\nTo Manually Run an Initial Backup Enter:\n" + echo -e "\t/usr/sbin/dirvish --vault $HN --init\n" + echo -e "\nPress Enter to Continue...\c" + read KEY + ;; + *) echo -e "\nInvalid Input..." + ;; + esac + else + FINISH=0 + until (( FINISH == 1 )) + do + echo -e "\nRun an Initial Backup Now? (y/n): \c" + read BACK_NOW + case $BACK_NOW in + y|Y) echo -e "\nExecuting Initial Dirvish Backup for $HN..." + dirvish --vault $HN --init + RC=$? + echo -e "\nInitial Backup for $HN Completed with a Return Code: $RC" + echo -e "\nPress Enter to Continue...\c" + read KEY + FINISH=1 + ;; + n|N) echo -e "\nInitial Backup Skipped..." + echo -e "\nDo Not Forget to Run an Initial Dirvish Backup!" + echo -e "\nTo Manually Run an Initial Backup Enter:\n" + echo -e "\tdirvish --vault $HN --init\n" + echo -e "\nPress Enter to Continue...\c" + read KEY + FINISH=1 + ;; + *) echo -e "Invalid Entry..." + sleep 2 + ;; + esac + done + fi + continue + ;; + 8) break + ;; + esac +done +} + +######################################## + +delete_backup () +{ +# This function is used to delete backups from the Dirvish server + +clear # Clear the screen +echo -e "\n\n\tREMOVE A SERVER FROM DIRVISH BACKUPS\n" +echo -e "\n\tEnter the Host to Remove from Dirvish Backups: \c" +read H + +BANK_LIST=$(parse_conf) + +for P in $BANK_LIST +do + VAULT_ID=$(find $P -type d -name "$H") + if [[ ! -z "$VAULT_ID" ]] + then + STOP=0 + until (( STOP == 1 )) + do + echo -e "\n\tRemove Backup Vault $H from the Dirvish Backup Server? (y/n): \c" + read ANS + case $ANS in + y|Y) echo -e "\n\tRemoving Backup Vault for $H from Dirvish Backups...\c" + rm -fr "$VAULT_ID" + echo -e "\n\tBackup Vault Removed...Press Enter to Continue...\c" + read KEY + STOP=1 + ;; + n|N) echo -e "\n\tVault Removal Canceled...Press Enter to Continue...\c" + read KEY + STOP=1 + continue + ;; + *) echo -e "\n\tInvalid Entry..." + ;; + esac + done + fi +done +} + +######################################## + +manage_banks () +{ +# This function is used to add and delete Dirvish banks + +# set -x # Uncomment to debug this function + +clear # Clear the screen` + +# Get a list of currently defined Dirvish banks +BANK_LIST=$(parse_conf) + +# Display the screen header information +echo -e "\n\n\tMANAGE DIRVISH BACKUP BANKS" +echo -e "\n\nCurrently Configured Backup Bank(s):\n" + +NO_BANK=0 + +# If this is an initial installation there will not +# be any Dirvish banks defined. + +if [ -z "$BANK_LIST" ] +then + NO_BANK=1 + echo -e "\nNo Backup Banks Have Been Defined in Dirvish\n" +else + BANK_FILE=/tmp/backlist.out + >$BANK_FILE + COUNT=0 + + for B in $BANK_LIST + do + ((COUNT == COUNT + 1)) + LAST_ENTRY=$B + echo -e "\t$B" | tee -a $BANK_FILE + done +fi + +# Display the menu options + +echo -e "\n\n1) Add a New Backup Bank" +echo -e "\n2) Delete a Current Backup Bank" +echo -e "\n3) Return to the Previous Menu" +echo -e "\n\nSelect an Option: \c" + +# Read the user input +read OPT +case $OPT in +1) # Add a New Backup Bank + echo -e "\nEnter the Bank to Add: \c" + read A_BANK + echo -e "\nAdding New Backup Bank: $A_BANK" + if (( NO_BANK == 0 )) + then + sed "s!$LAST_ENTRY!& \n\t${A_BANK}!g" $D_CONFIG > ${D_CONFIG}.modified + if (( $? == 0 )) + then + # Save the old Dirvish master config file with today's datestamp + cp ${D_CONFIG} ${D_CONFIG}.$(date +%m%d%Y) + cp ${D_CONFIG}.modified ${D_CONFIG} + + echo -e "\n$A_BANK Successfully Added to Dirvish Master Config File" + + # Check to see if the $A_BANK directoey exists, if not + # ask the user if it is okay to create it. + + chk_create_dir $A_BANK + else + echo -e "\nERROR: Adding $A_BANK Failed...See Administrator..." + fi + else + if $(grep -q "bank:" $D_CONFIG) + then + # NOTICE: It is important to note that sed does not "require" + # us to use / as a field separator. Here we are using ! as a + # sed field separator because we are working with UNIX directory + # paths, sed gets confused using / as a field separator. + + sed "s!bank:!& \n\t${A_BANK}!g" $D_CONFIG > ${D_CONFIG}.modified + + if (( $? == 0 )) + then + cp ${D_CONFIG} ${D_CONFIG}.$(date +%m%d%Y) + cp ${D_CONFIG}.modified ${D_CONFIG} + echo -e "\n$A_BANK Successfully Added to Dirvish Master Config File" + chk_create_dir $A_BANK + else + echo -e "\nERROR: Adding $A_BANK Failed...See Administrator..." + fi + else + echo -e "bank:\n\t$A_BANK" >> ${D_CONFIG}.modified + if (( $? == 0 )) + then + cp ${D_CONFIG} ${D_CONFIG}.$(date +%m%d%Y) + cp ${D_CONFIG}.modified ${D_CONFIG} + echo -e "\n$A_BANK Successfully Added to Dirvish Master Config File" + chk_create_dir $A_BANK + else + echo -e "\nERROR: Adding $A_BANK Failed...See Administrator..." + fi + fi + fi + + rm -f $BANK_FILE + + echo -e "\nPress Enter to Continue...\c" + read KEY + ;; +2) echo -e "\nEnter the Backup Bank to Remove: \c" + read R_BANK + if [ -d $R_BANK ] + then + POPULATED=$(ls $R_BANK | wc -l) + if (( POPULATED > 0 )) + then + echo -e "\nWARNING: The Bank $R_BANK has the Following Backup Images:\n" + ls $R_BANK | more + echo -e "\nAre you Sure you Want to Remove this Bank and all of the Backup Images? (y/n): \c" + read ANS + case $ANS in + y|Y) continue + ;; + n|N) break + ;; + *) echo -e "\nInvalid Input..." + ;; + esac + fi + fi + if $(cat "$BANK_FILE" | grep -q "$R_BANK") + then + if (( COUNT == 1 )) + then + echo -e "\nWARNING: $R_BANK is the Only Backup Bank Currently Configured!" + echo -e "\nRemoving this Bank Will Cripple Dirvish..." + fi + echo -e "\nAre you Sure You Want to Remove the $R_BANK Bank? (y/n): \c" + read ANS4 + case $ANS4 in + y|Y) cat $D_CONFIG | grep -v $R_BANK > ${D_CONFIG}.modified + cp -p $D_CONFIG ${D_CONFIG}.bak.$(date +%m%d%y) + cp ${D_CONFIG}.modified $D_CONFIG + echo -e "\n$R_BANK Removed from the Dirvish Configuration File..." + if [ -d $R_BANK ] + then + echo -e "\nDo You Want to Remove the $R_BANK Directory? (y/n): \c" + read ANS + case $ANS in + y|Y) rm -r $R_BANK + if (( $? == 0 )) + then + echo -e "\n$R_BANK Directory Removed Successully" + else + echo -e "\nERROR: Remove $R_BANK Directory Failed" + fi + ;; + n|N) echo -e "\nSkipping $R_BANK Directory Removal" + ;; + *) echo -e "\nInvalid Input..." + ;; + esac + fi + ;; + n|N) echo -e "\nSkipping Bank Removal\n" + ;; + *) echo -e "\nInvalid Entry..." + ;; + esac + else + echo -e "\nERROR: $R_BANK is Not a Valid Bank" + fi + + echo -e "\nPress Enter to Continue...\c" + read KEY + + ;; +3) continue + ;; +*) echo -e "\nInvalid Entry...\n" + ;; +esac +} + +######################################## + +check_root_user () +{ +case $(uname) in +SunOS) if [[ $LOGNAME != root ]] + then + echo -e "\n\n\tERROR: Only the root User can Execute this Program..." + echo -e "\n\n\t...EXITING...\n" + exit 1 + fi + ;; + *) if [[ $(whoami) != root ]] + then + echo -e "\n\n\tERROR: Only the root User can Execute this Program..." + echo -e "\n\n\t...EXITING...\n" + exit 1 + fi +esac +} + +######################################## + +check_config () +{ +# Find the Dirvish Master Configuration File + +if [ -r /etc/dirvish.conf ] +then + M_CONFIG=/etc/dirvish.conf + +elif [ -r /etc/dirvish/master.conf ] +then + M_CONFIG=/etc/dirvish/master.conf +else + echo -e "\n\n\tERROR: Dirvish is not configured on this system" + echo -e "\tTo use this program install rsync and dirvish first." + echo -e "\n\t...Exiting...\n" + exit 2 +fi +} + +######################################## +# BEGINNING OF MAIN +######################################## + +check_root_user + +check_config + +until (( DONE == 1 )) +do + display_main_menu + read OPTION + case $OPTION in + 1) # Dirvish RunAll + run_all + ;; + 2) # Dirvish Run Backup + run_backup + ;; + 3) # Dirvish Locate Image + locate_restore_image + ;; + 4) # Dirvish Expire Backup(s) + expire_backup + ;; + 5) # Dirvish Add Backup + add_backup + ;; + 6) # Dirvish Delete Backup + delete_backup + ;; + 7) # Manage Backup Banks + manage_banks + ;; + 8) clear + DONE=1 + ;; + *) echo -e "\n\tERROR: Invalid Entry..." + sleep 2 + ;; + esac +done diff --git a/chapter27/function_add_backup b/chapter27/function_add_backup new file mode 100755 index 0000000..4e9dd3a --- /dev/null +++ b/chapter27/function_add_backup @@ -0,0 +1,218 @@ +add_backup () +{ +# This function is used to define a new backup to Dirvish + +# set -x # Uncomment to debug this function + +# Get a list of available banks + +BANK_LIST=$(parse_conf) + +ESCAPE=0 +until (( ESCAPE == 1 )) +do + clear + echo -e " + \n\n\t\t DIRVISH ADD A BACKUP + \n\tSelect Each Option to Add a New Backup + \n\t1) Enter Hostname + \n\t2) Select a Bank for this Backup + \n\t3) Enter Directory Tree to Back up + \n\t4) Enter Directory Trees to Ignore + \n\t5) Enter Days to Retain Each Backup + \n\t6) Add this New Dirvish Backup Definition + \n\t7) Create the Initial Dirvish Backup + \n\t8) Main Menu + \n\n\tSelect Each Option to Add a New Backup + \n\n\tEnter Option: \c" + + read OPTION # Read in the user response + + # Perform the desired operation + case $OPTION in + 1) echo -e "\n\tEnter the Hostname for this Backup: \c" + read HN + for B in $BANK_LIST + do + P=$(find $B -type d -name $HN) + if [ ! -z $P ] + then + echo -e "\n\tWARNING: Vault $HN Already Exists in the Following Bank:" + echo -e "\n\t$P" + echo -e "\n\tDo you want to create a new default.conf file for $HN? (y/n): \c" + ANS=n # Set the default answer to 'n', No. + read ANS + case $ANS in + y|Y) continue + ;; + n|N) return + ;; + esac + fi + done + ;; + 2) FINISH=0 + until (( FINISH == 1 )) + do + clear + BANK= + echo -e "\n\n\t\tSELECT A BANK TO STORE THIS BACKUP\n" + echo -e "\nAvailable Banks:\n" + for B in $BANK_LIST + do + echo -e "\t$B" + done + echo -e "\nPlease Enter a Bank: \c:" + read BANK + if $(echo $BANK_LIST | grep -q ${BANK}) + then + echo "$BANK selected for $HN" + FINISH=1 + else + echo -e "\nERROR: $BANK is not a Valid Bank" + sleep 2 + continue + fi + done + ;; + 3) echo -e "\n\tEnter the Directoy Tree to Backup: \c" + read TREE + continue + ;; + 4) clear + echo -e "\n\n\tDIRECTORY TREE(S) and FILES TO IGNORE\n" + echo -e "\nThe Specified Directory Tree Contains the Following Files/Directories:\n\n" + if [ $HN = $(hostname) ] + then + ls $TREE | more + else + ssh $HN ls $TREE | more + fi + echo -e "\nDo you want to Exclude any Files or Directories from the Backups? (y/n): \c" + read ANS3 + case $ANS3 in + y|Y) echo -e "\n\tEnter Directories and Files to Ignore One per Line\n" + echo -e "Enter 99 when finished\n" + IGNORE_LIST=$(read_input) + continue + ;; + n|N) IGNORE_LIST=$(echo) + continue + ;; + *) echo -e "\nInvalid Entry..." + ;; + esac + ;; + 5) echo -e "\n\tDays before each Backup Expires: \c" + read DAYS + continue + ;; + 6) clear + echo -e "\n\n\tADD NEW BACKUP TO DIRVISH" + if [[ -r "${BANK}/${HN}/dirvish/default.conf" ]] + then + echo -e "\nWARNING: default.conf File Exists...Rebuild default.conf? (y/n): \c" + read ANS + case $ANS in + y|Y) echo -e "\n\nCreating default.conf Dirvish Configuration File for $HN" + build_default_conf $HN "$BANK" $DAYS "$TREE" "$IGNORE_LIST" + echo -e "\nCopying file to: ${BANK}/${HN}/dirvish/default.conf" + mkdir -p ${BANK}/${HN}/dirvish + cp -f $DEFAULT_CONF ${BANK}/${HN}/dirvish/default.conf + echo -e "\n\n...Press ENTER to Continue...\c" + read ANS + ;; + *) break + ;; + esac + else + echo -e "\n\nCreating default.conf Dirvish Configuration File for $HN" + build_default_conf $HN "$BANK" $DAYS "$TREE" "$IGNORE_LIST" + echo -e "\nCopying file to: ${BANK}/${HN}/dirvish/default.conf" + mkdir -p ${BANK}/${HN}/dirvish + cp -f $DEFAULT_CONF ${BANK}/${HN}/dirvish/default.conf + echo -e "\n...Press ENTER to Continue...\c" + read ANS + fi + ;; + 7) clear + echo -e "\n\n\tCREATE INITIAL DIRVISH BACKUP" + echo -e "\n" + if [[ ! -d "${BANK}/${HN}" ]] + then + echo -e "\nCreating Vault $HN for this Backup" + mkdir -p "${BANK}/${HN}" + mkdir -p "${BANK}/${HN}/dirvish" + else + echo -e "\nBackup Vault for $HN Exists...Continuing..." + fi + if [[ ! -r "${BANK}/${HN}/dirvish/default.conf" ]] + then + # Now we have enough information to build the + # new default.conf file + + echo -e "Creating default.conf Dirvish Configuration File for $HN" + + build_default_conf $HN "$BANK" $DAYS "$TREE" "$IGNORE_LIST" + + echo -e "\nCopying file to: ${BANK}/${HN}/dirvish/default.conf" + cp -f $DEFAULT_CONF ${BANK}/${HN}/dirvish/default.conf + + # Now we need to run an initial backup + echo -e "\nRun an Initial Backup Now? (y/n): \c" + read BACK_NOW + case $BACK_NOW in + y|Y) echo -e "\nExecuting Initial Dirvish Backup for $HN..." + dirvish --vault $HN --init + RC=$? + echo -e "\nInitial Backup for $HN Completed with a Return Code: $RC" + echo -e "\nPress Enter to Continue...\c" + read KEY + ;; + n|N) echo -e "\nInitial Backup Skipped..." + echo -e "\nDo Not Forget to Run an Initial Dirvish Backup!" + echo -e "\nTo Manually Run an Initial Backup Enter:\n" + echo -e "\t/usr/sbin/dirvish --vault $HN --init\n" + echo -e "\nPress Enter to Continue...\c" + read KEY + ;; + *) echo -e "\nInvalid Input..." + ;; + esac + else + FINISH=0 + until (( FINISH == 1 )) + do + echo -e "\nRun an Initial Backup Now? (y/n): \c" + read BACK_NOW + case $BACK_NOW in + y|Y) echo -e "\nExecuting Initial Dirvish Backup for $HN..." + dirvish --vault $HN --init + RC=$? + echo -e "\nInitial Backup for $HN Completed with a Return Code: $RC" + echo -e "\nPress Enter to Continue...\c" + read KEY + FINISH=1 + ;; + n|N) echo -e "\nInitial Backup Skipped..." + echo -e "\nDo Not Forget to Run an Initial Dirvish Backup!" + echo -e "\nTo Manually Run an Initial Backup Enter:\n" + echo -e "\tdirvish --vault $HN --init\n" + echo -e "\nPress Enter to Continue...\c" + read KEY + FINISH=1 + ;; + *) echo -e "Invalid Entry..." + sleep 2 + ;; + esac + done + fi + continue + ;; + 8) break + ;; + esac +done +} + diff --git a/chapter27/function_build_default_conf b/chapter27/function_build_default_conf new file mode 100755 index 0000000..461acfd --- /dev/null +++ b/chapter27/function_build_default_conf @@ -0,0 +1,31 @@ +build_default_conf () +{ +# Define files and variables here + +HN="$1" # Hostname of server to add +BANK="$2" # Bank to use for backup +DAYS="$3" # Days before backups expire +TREE="$4" # Directory tree to back up +IGNORE_LIST="$5" # Files and directories to ignore +OUTFILE=$DEFAULT_CONF # Name of the output file + +# All of the following output is used to build the +# new default.conf file for this backup +{ +echo "client: $HN" +echo "bank: $BANK" +echo "vault: $HN" +echo "server: $DIRVISH_SERVER" +echo "expire: $DAYS day" +echo "index: gzip" +echo "log: gzip" +echo "image: ${HN}-%y%m%d%H%M%S" +echo "tree: $TREE" +echo -e "\nexclude:" +echo "$IGNORE_LIST" | sed /^$/d | while read Q +do + echo -e "\t$Q" +done +} >$OUTFILE +} + diff --git a/chapter27/function_check_config b/chapter27/function_check_config new file mode 100755 index 0000000..ff3160d --- /dev/null +++ b/chapter27/function_check_config @@ -0,0 +1,19 @@ +check_config () +{ +# Find the Dirvish Master Configuration File + +if [ -r /etc/dirvish.conf ] +then + M_CONFIG=/etc/dirvish.conf + +elif [ -r /etc/dirvish/master.conf ] +then + M_CONFIG=/etc/dirvish/master.conf +else + echo -e "\n\n\tERROR: Dirvish is not configured on this system" + echo -e "\tTo use this program install rsync and dirvish first." + echo -e "\n\t...Exiting...\n" + exit 2 +fi +} + diff --git a/chapter27/function_check_root_user b/chapter27/function_check_root_user new file mode 100755 index 0000000..92dc446 --- /dev/null +++ b/chapter27/function_check_root_user @@ -0,0 +1,19 @@ +check_root_user () +{ +case $(uname) in +SunOS) if [[ $LOGNAME != root ]] + then + echo -e "\n\n\tERROR: Only the root User can Execute this Program..." + echo -e "\n\n\t...EXITING...\n" + exit 1 + fi + ;; + *) if [[ $(whoami) != root ]] + then + echo -e "\n\n\tERROR: Only the root User can Execute this Program..." + echo -e "\n\n\t...EXITING...\n" + exit 1 + fi +esac +} + diff --git a/chapter27/function_chk_create_dir b/chapter27/function_chk_create_dir new file mode 100755 index 0000000..5c36734 --- /dev/null +++ b/chapter27/function_chk_create_dir @@ -0,0 +1,30 @@ +chk_create_dir () +{ +# This function is used to add a new Dirvish "bank" to +# the Dirvish backup server, which should be this machine + +D=$1 # Directory name to add + +if [ ! -d $D ] # Check if the directory already exists +then + echo -e "\n$D Directory Does Not Exist...Create $D Directory? (y/n): \c" + read ANS + case $ANS in + y|Y) echo -e "\nCreating $D Directory..." + mkdir -p $D + if (( $? == 0 )) + then + echo -e "\n$D Directory Created" + else + echo -e "\n$D Directory Creation Failed...See Administrator..." + fi + ;; + n|N) echo -e "\nSkipping Creating the $D Directory" + echo -e "\nWARNING: Until the $D Directory is Created Dirvish Cannot use this Bank\n" + ;; + *) echo -e "\nInvalid Input..." + ;; + esac +fi +} + diff --git a/chapter27/function_delete_backup b/chapter27/function_delete_backup new file mode 100755 index 0000000..af3e41f --- /dev/null +++ b/chapter27/function_delete_backup @@ -0,0 +1,41 @@ +delete_backup () +{ +# This function is used to delete backups from the Dirvish server + +clear # Clear the screen +echo -e "\n\n\tREMOVE A SERVER FROM DIRVISH BACKUPS\n" +echo -e "\n\tEnter the Host to Remove from Dirvish Backups: \c" +read H + +BANK_LIST=$(parse_conf) + +for P in $BANK_LIST +do + VAULT_ID=$(find $P -type d -name "$H") + if [[ ! -z "$VAULT_ID" ]] + then + STOP=0 + until (( STOP == 1 )) + do + echo -e "\n\tRemove Backup Vault $H from the Dirvish Backup Server? (y/n): \c" + read ANS + case $ANS in + y|Y) echo -e "\n\tRemoving Backup Vault for $H from Dirvish Backups...\c" + rm -fr "$VAULT_ID" + echo -e "\n\tBackup Vault Removed...Press Enter to Continue...\c" + read KEY + STOP=1 + ;; + n|N) echo -e "\n\tVault Removal Canceled...Press Enter to Continue...\c" + read KEY + STOP=1 + continue + ;; + *) echo -e "\n\tInvalid Entry..." + ;; + esac + done + fi +done +} + diff --git a/chapter27/function_display_main_menu b/chapter27/function_display_main_menu new file mode 100755 index 0000000..4b3aa2e --- /dev/null +++ b/chapter27/function_display_main_menu @@ -0,0 +1,31 @@ +display_main_menu () +{ +# This function displays the main menu + +clear # Clear the screen + +# Display the menu header and menu + +echo -e "\n\n\tWELCOME TO DIRVISH BACKUP COMMAND-CENTER + + +\t1) Dirvish RunAll Backups + +\t2) Dirvish Run Backup + +\t3) Dirvish Locate/Restore Image + +\t4) Dirvish Expire/Delete Backup(s) + +\t5) Dirvish Add a New Backup + +\t6) Dirvish Remove a Backup + +\t7) Dirvish Manage Backup Banks + +\t8) EXIT + +" +echo -e "\tSelect an Option: \c" +} + diff --git a/chapter27/function_expire_backup b/chapter27/function_expire_backup new file mode 100755 index 0000000..44937d9 --- /dev/null +++ b/chapter27/function_expire_backup @@ -0,0 +1,164 @@ +expire_backup () +{ +# This function is used to expire backups + +# set -x # Uncomment to debug this function + +clear # Clear the screen + +# Display the screen header and menu + +echo -e " + \n\n\t\tDIRVISH EXPIRE BACKUP(S)\n + \n\t1) Delete Expired Backups for ALL Hosts + \n\t2) Delete Expired Backups for One Host + \n\t3) Expire One Backup for One Host + \n\t4) Expire All Backups for One Host + \n\t5) Previous Menu + \n\n\tSelect an Option: \c" + +read ANS +case $ANS in +1) echo -e "\n\tDeleting Expired Backups for ALL Hosts..." + dirvish-expire --tree + echo -e "\n\tTasks Complete..." + echo -e "\n\tPress Enter to Continue...\c" + read KEY + ;; +2) echo -e "\n\tEnter the Hostname to Delete Expired Backups: \c" + read H + dirvish-expire --tree --vault $H | more + echo -e "\n\tTasks Complete..." + echo -e "\n\tPress Enter to Continue...\c" + read KEY + ;; +3) EXPIRE_STAMP="$(date "+%Y-%m-%d %H:%M:%S")" + echo -e "\n\tEnter the Hostname for the Backup Image: \c" + read H + + BANK_LIST=$(parse_conf) + MYPWD=$(pwd) + + for B in $BANK_LIST + do + if [ -d ${B}/${H} ] + then + cd ${B}/${H} + IMAGE_LIST=$(find . -type d -name "*-[0-9][0-9]*" | cut -c3-) + fi + done + + echo -e "\nSelect an Image to Expire from the Following List:\n" + echo -e "\n$IMAGE_LIST" | more + echo -e "\n\nEnter the Image to Expire: \c" + read IMAGE_NAME + + cd $MYPWD + if [[ -r ${B}/${H}/${IMAGE_NAME}/summary ]] + then + # This is where we expire a backup that is not currently + # expired. The 'summary' file contains the expire datestamp + + echo -e "\n\tExpiring Image: $IMAGE_NAME" + + sed s/"$(grep Expire ${B}/${H}/${IMAGE_NAME}/summary)"/"$(grep Expire ${B}/${H}/${IMAGE_NAME}/summary | cut -f 1-4 -d ' ') $EXPIRE_STAMP"/g "${B}/${H}/${IMAGE_NAME}/summary" > "${B}/${H}/${IMAGE_NAME}/summary2" + + mv "${B}/${H}/${IMAGE_NAME}/summary2" "${B}/${H}/${IMAGE_NAME}/summary" + fi + + # We expired the backups; now we ask the user if we should delete + # the expired images + + STOP=0 + until (( STOP == 1 )) + do + echo -e "\n\tTasks Complete...Do You Want to Delete This Expired Image? (y/n): \c" + read ANS + case $ANS in + y|Y) echo -e "\n\tDeleting Expired Image: $IMAGE_NAME" + + # This next command deletes all expired images for $H + dirvish-expire --vault $H + + RC=$? + echo -e "\n\tDirvish-expire Completed with Return Code: $RC" + echo -e "\n\tTasks Complete..." + echo -e "\n\tPress Enter to Continue...\c" + read KEY + STOP=1 + ;; + n|N) STOP=1 + continue + ;; + *) echo -e "\n\tInvalid Entry..." + ;; + esac + done + ;; +4) EXPIRE_STAMP=$(date "+%Y-%m-%d %H:%M:%S") + clear + echo -e "\nEnter the Hostname for the Backup Image: \c" + read H + + BANK_LIST=$(parse_conf) + MYPWD=$(pwd) + + for P in $BANK_LIST + do + if [ -d ${P}/${H} ] + then + cd ${P}/${H} + IMAGE_LIST=$(find . -type d -name "*-[0-9][0-9]*" | cut -c3-) + B=$P + fi + done + + echo -e "\nSetting to Following Images to Expire:\n\n" + echo "$IMAGE_LIST" | more + cd $MYPWD + echo -e "\nPress Enter to Continue...\c" + + echo "$IMAGE_LIST" | while read IMAGE_NAME + do + if [ -f ${B}/${H}/${IMAGE_NAME}/summary ] + then + echo "Expiring $IMAGE_NAME" + sed s/"$(grep Expire ${B}/${H}/${IMAGE_NAME}/summary)"/"$(grep Expire ${B}/${H}/${IMAGE_NAME}/summary | cut -f 1-4 -d ' ') $EXPIRE_STAMP"/g "${B}/${H}/${IMAGE_NAME}/summary" > "${B}/${H}/${IMAGE_NAME}/summary2" + + if (( $? == 0 )) + then + mv ${B}/${H}/${IMAGE_NAME}/summary2 ${B}/${H}/${IMAGE_NAME}/summary + fi + fi + done + + STOP=0 + until (( STOP == 1 )) + do + echo -e "\nTasks Complete...Do You Want to Delete Expired Images? (y/n): \c" + read ANS + case $ANS in + y|Y) echo -e "\nDeleting Expired Images...\n" + dirvish-expire --vault $H + RC=$? + echo -e "\nDirvish-expire Completed with Return Code: $RC" + echo -e "\nTasks Complete..." + echo -e "\nPress Enter to Continue...\c" + read KEY + STOP=1 + ;; + n|N) STOP=1 + continue + ;; + *) echo -e "\n\tInvalid Entry..." + ;; + esac + done + ;; +5) : + ;; +*) echo -e "\nInvalid Entry" + ;; +esac +} + diff --git a/chapter27/function_locate_restore_image b/chapter27/function_locate_restore_image new file mode 100755 index 0000000..ca55692 --- /dev/null +++ b/chapter27/function_locate_restore_image @@ -0,0 +1,153 @@ +locate_restore_image () +{ +# set -x # Uncomment to debug this function + +clear # Clear the screen + +# Display the screen heading, query the user, display the +# files matching the search, and ask if the user wants to +# restore the files. If a restore is requested we ensure +# there is a directory for the target server in the $RESTORE_AREA, +# if not create it, and copy the files into this subdirectory in +# the $RESTORE_AREA. + +echo -e "\n\n\t\tLOCATE/RESTORE A BACKUP IMAGE\n" +echo -e "Enter the Hostname where the File/Directory Resides: \c" +read H +echo -e "\nEnter as Much of the Directory path and File Name as You Know:\n" +read "DP" +if [ "$DP" = '/' ] +then + echo -e "\nERROR: This Program is Not Intended for Full System Restores...\n" + sleep 2 + return +fi +echo -e "\nSearching for Archive Images..." + +# Search and display all matching files one page at a time + +dirvish-locate $H ${DP} | more + +QUIT=0 +until (( QUIT == 1 )) +do + echo -e "\nDid You Find What You Want? (y/n): \c" + read ANS1 + case $ANS1 in + y|Y) echo -e "\nDo You Want to Perform a Restore? (y/n): \c" + read ANS2 + while true + do + case $ANS2 in + y|Y) QUIT=1 + break + ;; + n|N) echo -e "\nPress Enter to Continue...\c" + read KEY + return 0 + ;; + *) echo -e "\nInvalid Input..." + ;; + esac + done + ;; + n|N) return 0 + ;; + *) echo -e "\nInvalid Input..." + ;; + esac +done + +echo -e "\nEnter the FULL PATHNAME of the File/Directory You Want to Restore:\n" +read TARGET + +if [ -z "$TARGET" ] +then + echo -e "\nInvalid Entry..." + sleep 2 + return +fi + +if [[ $(echo "$TARGET" | cut -c1) != '/' ]] +then + echo -e "\nERROR: $TARGET is Not a Valid Full Pathname...\n" + sleep 2 + break +fi + +echo -e "\nSearching Images..." + +dirvish-locate "$H" "${TARGET}" | more + +echo -e "\nEnter the Image to Restore From: \c" +read IMAGE_NAME +echo + +# Check to ensure that the defined $RESTORE_AREA +# directory has been created on the system. + +if [[ ! -d $RESTORE_AREA ]] +then + mkdir -p $RESTORE_AREA +fi + +# Ensure that a subdirectory exists for this host + +if [[ ! -d ${RESTORE_AREA}/${H} ]] +then + mkdir -p ${RESTORE_AREA}/${H} +fi + +BANK_LIST=$(parse_conf) + +# This is where we restore the data + +for BANK in $BANK_LIST +do + if [ -d "$TARGET" ] + then + # If $TARGET is a directory we only want the last + # part of the path, not the entire path. + + TARGET=$(basename "$TARGET") + + find ${BANK}/${H}/${IMAGE_NAME}/tree -name "${TARGET}*" -print \ + -exec cp -rp {} "${RESTORE_AREA}/${H}" \; 2>/dev/null + + if (( $? == 0 )) + then + echo -e "\nFiles Restored to the Following Restore Area:" + echo -e "\n${RESTORE_AREA}/${H}/${TARGET}\n\n" + break + else + echo -e "\nRestore Failed...See Administrator...\n" + break + fi + else + # We are working with a file + + D_NAME=$(dirname "$TARGET" | cut -d '/' -f3-) + F_NAME=$(basename "$TARGET") + echo "DIRNAME is $D_NAME" + + # Find and copy the file(s) to the $RESTORE_AREA + + find ${BANK}/${H}/${IMAGE_NAME}/tree/*${D_NAME} -name "${F_NAME}" \ + -print -exec cp -rp {} "${RESTORE_AREA}/${H}" \; 2>/dev/null + + if (( $? == 0 )) + then + echo -e "\nFiles Restored to the Following Restore Area:" + echo -e "\n${RESTORE_AREA}/${H}/${F_NAME}\n\n" + break + else + echo -e "\nRestore Failed...See Administrator...\n" + break + fi + fi +done + +echo -e "\nPress Enter to Continue...\c" +read KEY +} + diff --git a/chapter27/function_manage_banks b/chapter27/function_manage_banks new file mode 100755 index 0000000..d4c6276 --- /dev/null +++ b/chapter27/function_manage_banks @@ -0,0 +1,182 @@ +manage_banks () +{ +# This function is used to add and delete Dirvish banks + +# set -x # Uncomment to debug this function + +clear # Clear the screen` + +# Get a list of currently defined Dirvish banks +BANK_LIST=$(parse_conf) + +# Display the screen header information +echo -e "\n\n\tMANAGE DIRVISH BACKUP BANKS" +echo -e "\n\nCurrently Configured Backup Bank(s):\n" + +NO_BANK=0 + +# If this is an initial installation there will not +# be any Dirvish banks defined. + +if [ -z "$BANK_LIST" ] +then + NO_BANK=1 + echo -e "\nNo Backup Banks Have Been Defined in Dirvish\n" +else + BANK_FILE=/tmp/backlist.out + >$BANK_FILE + COUNT=0 + + for B in $BANK_LIST + do + ((COUNT == COUNT + 1)) + LAST_ENTRY=$B + echo -e "\t$B" | tee -a $BANK_FILE + done +fi + +# Display the menu options + +echo -e "\n\n1) Add a New Backup Bank" +echo -e "\n2) Delete a Current Backup Bank" +echo -e "\n3) Return to the Previous Menu" +echo -e "\n\nSelect an Option: \c" + +# Read the user input +read OPT +case $OPT in +1) # Add a New Backup Bank + echo -e "\nEnter the Bank to Add: \c" + read A_BANK + echo -e "\nAdding New Backup Bank: $A_BANK" + if (( NO_BANK == 0 )) + then + sed "s!$LAST_ENTRY!& \n\t${A_BANK}!g" $D_CONFIG > ${D_CONFIG}.modified + if (( $? == 0 )) + then + # Save the old Dirvish master config file with today's datestamp + cp ${D_CONFIG} ${D_CONFIG}.$(date +%m%d%Y) + cp ${D_CONFIG}.modified ${D_CONFIG} + + echo -e "\n$A_BANK Successfully Added to Dirvish Master Config File" + + # Check to see if the $A_BANK directoey exists, if not + # ask the user if it is okay to create it. + + chk_create_dir $A_BANK + else + echo -e "\nERROR: Adding $A_BANK Failed...See Administrator..." + fi + else + if $(grep -q "bank:" $D_CONFIG) + then + # NOTICE: It is important to note that sed does not "require" + # us to use / as a field separator. Here we are using ! as a + # sed field separator because we are working with UNIX directory + # paths, sed gets confused using / as a field separator. + + sed "s!bank:!& \n\t${A_BANK}!g" $D_CONFIG > ${D_CONFIG}.modified + + if (( $? == 0 )) + then + cp ${D_CONFIG} ${D_CONFIG}.$(date +%m%d%Y) + cp ${D_CONFIG}.modified ${D_CONFIG} + echo -e "\n$A_BANK Successfully Added to Dirvish Master Config File" + chk_create_dir $A_BANK + else + echo -e "\nERROR: Adding $A_BANK Failed...See Administrator..." + fi + else + echo -e "bank:\n\t$A_BANK" >> ${D_CONFIG}.modified + if (( $? == 0 )) + then + cp ${D_CONFIG} ${D_CONFIG}.$(date +%m%d%Y) + cp ${D_CONFIG}.modified ${D_CONFIG} + echo -e "\n$A_BANK Successfully Added to Dirvish Master Config File" + chk_create_dir $A_BANK + else + echo -e "\nERROR: Adding $A_BANK Failed...See Administrator..." + fi + fi + fi + + rm -f $BANK_FILE + + echo -e "\nPress Enter to Continue...\c" + read KEY + ;; +2) echo -e "\nEnter the Backup Bank to Remove: \c" + read R_BANK + if [ -d $R_BANK ] + then + POPULATED=$(ls $R_BANK | wc -l) + if (( POPULATED > 0 )) + then + echo -e "\nWARNING: The Bank $R_BANK has the Following Backup Images:\n" + ls $R_BANK | more + echo -e "\nAre you Sure you Want to Remove this Bank and all of the Backup Images? (y/n): \c" + read ANS + case $ANS in + y|Y) continue + ;; + n|N) break + ;; + *) echo -e "\nInvalid Input..." + ;; + esac + fi + fi + if $(cat "$BANK_FILE" | grep -q "$R_BANK") + then + if (( COUNT == 1 )) + then + echo -e "\nWARNING: $R_BANK is the Only Backup Bank Currently Configured!" + echo -e "\nRemoving this Bank Will Cripple Dirvish..." + fi + echo -e "\nAre you Sure You Want to Remove the $R_BANK Bank? (y/n): \c" + read ANS4 + case $ANS4 in + y|Y) cat $D_CONFIG | grep -v $R_BANK > ${D_CONFIG}.modified + cp -p $D_CONFIG ${D_CONFIG}.bak.$(date +%m%d%y) + cp ${D_CONFIG}.modified $D_CONFIG + echo -e "\n$R_BANK Removed from the Dirvish Configuration File..." + if [ -d $R_BANK ] + then + echo -e "\nDo You Want to Remove the $R_BANK Directory? (y/n): \c" + read ANS + case $ANS in + y|Y) rm -r $R_BANK + if (( $? == 0 )) + then + echo -e "\n$R_BANK Directory Removed Successully" + else + echo -e "\nERROR: Remove $R_BANK Directory Failed" + fi + ;; + n|N) echo -e "\nSkipping $R_BANK Directory Removal" + ;; + *) echo -e "\nInvalid Input..." + ;; + esac + fi + ;; + n|N) echo -e "\nSkipping Bank Removal\n" + ;; + *) echo -e "\nInvalid Entry..." + ;; + esac + else + echo -e "\nERROR: $R_BANK is Not a Valid Bank" + fi + + echo -e "\nPress Enter to Continue...\c" + read KEY + + ;; +3) continue + ;; +*) echo -e "\nInvalid Entry...\n" + ;; +esac +} + diff --git a/chapter27/function_parse_conf b/chapter27/function_parse_conf new file mode 100755 index 0000000..766a74c --- /dev/null +++ b/chapter27/function_parse_conf @@ -0,0 +1,42 @@ +parse_conf () +{ +# This function parses through the Dirvish Master +# Configuration File, specified by $M_CONFIG, to +# gather a list of all of the defined Dirvish banks. + +# set -x # Uncomment to debug this function + +# Initial local variables + +BANK_LIST= +START=0 + +# Loop through the $M_CONFIG file until we find +# the 'bank:' stanza, then add each bank to a list +# until we reach another stanza, 'stanza:' +# Loop through the $M_CONFIG file until we find +# the 'bank:' stanza, then add each bank to a list +# until we reach another stanza, 'stanza:' + +while read DATA +do + [[ $DATA = 'bank:' ]] && START=1 && continue + if (( START == 1 )) + then + if $(echo "$DATA" | grep -q ':') + then + break + else + if [[ -z "$BANK_LIST" ]] + then + BANK_LIST="$DATA" + else + BANK_LIST="$BANK_LIST $DATA" + fi + fi + fi +done < $M_CONFIG # Feed the while loop from the bottom + +echo "$BANK_LIST" # Return the list +} + diff --git a/chapter27/function_read_input b/chapter27/function_read_input new file mode 100755 index 0000000..3608070 --- /dev/null +++ b/chapter27/function_read_input @@ -0,0 +1,19 @@ +read_input () +{ +# This function is used to save user input + +LINE= # Initialize LINE to NULL + +while true +do + read "ENTRY" + if [[ $ENTRY = 99 ]] + then + echo "$LINE" + break + fi + LINE="$LINE +$ENTRY" +done +} + diff --git a/chapter27/function_run_all b/chapter27/function_run_all new file mode 100755 index 0000000..d7b83bf --- /dev/null +++ b/chapter27/function_run_all @@ -0,0 +1,21 @@ +run_all () +{ +# This function runs all of the backups defined in +# the Dirvish master configuration file stanza "Runall:" + +clear # Clear the screen +echo -e "\n\n" + +# Execute all master.conf defined backups + +dirvish-runall + +if (( $? == 0 )) +then + echo -e "\n\tBackups Complete...Press Enter to Continue...\c" +else + echo -e "\n\tDirvish Backup ERROR...Press Enter to Continue...\c" +fi +read KEY +} + diff --git a/chapter27/function_run_backup b/chapter27/function_run_backup new file mode 100755 index 0000000..8876b80 --- /dev/null +++ b/chapter27/function_run_backup @@ -0,0 +1,50 @@ +run_backup () +{ +# This function runs one particular backup + +# set -x # Uncomment to debug this function + +clear # Clear the screen + +# Display the screen heading and query the user + +echo -e "\n\n\t\tRUN A PARTICULAR BACKUP\n" +echo -e "\tEnter a Hostname to Backup: \c" +read HTBU # Host to Backup + +echo "Searching for default.conf in ${HTBU}'s Vault" + +BANK_LIST=$(parse_conf) + +for P in $BANK_LIST +do + # Find the Default Config file (default.conf) for $HTBU + + DCF=$(find ${P}/${HTBU} -type f -name default.conf) + if [[ ! -z $DCF ]] + then + echo -e "\nFound Configuration File...Starting Backup..." + dirvish --vault $HTBU + RC=$? + echo -e "\nDirvish Exit Code: $RC" + echo -e "\nBackup Complete..." + echo -e "\nPress Enter to Continue...\c" + read KEY + break + else + echo -e "\nERROR: Could not Locate the Configuration File for $HTBU" + echo -e "\n...You Need to Configure $HTBU for Dirvish Backup First" + echo -e "\nPress Enter to Continue...\c" + read KEY + fi +done + +if [[ -z "$DCF" ]] +then + echo -e "\nERROR: Could not Locate the Configuration File for $HTBU" + echo -e "\n...You Need to Configure $HTBU for Dirvish Backup First" + echo -e "\nPress Enter to Continue...\c" + read KEY +fi +} + diff --git a/chapter28/boracle b/chapter28/boracle new file mode 100755 index 0000000..597fd50 --- /dev/null +++ b/chapter28/boracle @@ -0,0 +1,123 @@ +#!/bin/ksh +# +# SCRIPT: "Banybody" boracle - This time +# +# AUTHOR: Randy Michael +# DATE: 05/08/2007 +# REV: 1.0.P +# PLATFOEM: Any UNIX +# +# PURPOSE: This shell script is used to capture all "$EFF_USER" access by +# capturing all of the terminal data in a log file using +# the script command. This shell script is executed from the +# command line using sudo (Super User Do). The log file +# is kept locally and e-mailed to a log file administrative +# user either locally or on a remote machine. Sudo must be +# configured for this shell script. Refer to your sudo notes. +# The effective user, currently oracle, can be changed by +# setting the "EFF_USER" variable to another user, and changing +# the name of the script. This is why the original name of the +# script is called "Banybody" +# +# ORIGINAL USAGE: sudo Banybody +# +# THIS TIME USAGE ==> USAGE: sudo boracle +# +# +# REV LIST: +# 5/10/2007: Modified the script to replace the hardcoded username +# with the variable $EFF_USER. This allows flexibility +# to add auditing of more accounts be just changing +# the EFF_USER variable and the script name. +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this shell script +# +# +################# DEFINE EFFECTIVE USER ################## + +# This EFF_USER is the user name you want to be to execute +# a shell in. An su command is used to switch to this user. + +EFF_USER=oracle + +############# DEFINE AUDIT LOG MANAGER ################### + +# This user receives all of the audit logs by e-mail. This +# Log Manager can have a local or remote e-mail address. You +# can add more than one e-mail address if you want by separating +# each address with a space. + +LOG_SERVER=yogi +LOG_MANAGER="logman@$LOG_SERVER" # List to email audit log + +# Set up the correct echo command usage. Some Linux machines +# may execute all scripts in Bash shell. + +case $(basename $SHELL) in +bash) alias echo="echo -e" + ;; +esac +########################################################## +################ DEFINE FUNCTIONS HERE ################### +########################################################## + +cleanup_exit () +{ +# This function is executed on any type of exit except of course +# a kill -9, which cannot be trapped. The script log file is +# emailed either locally or remotely, and the log file is +# compressed. The last "exit" is needed so the user does not +# have the ability to get to the command line without logging. + +if [[ -s ${LOGDIR}/${LOGFILE} ]] # Is it greater than zero bytes? +then + mailx -s "$TS - $LOGNAME Audit Report" $LOG_MANAGER < ${LOGDIR}/${LOGFILE} + nohup compress ${LOGDIR}/${LOGFILE} & +fi +} + +# Set a trap + +trap 'cleanup_exit' 1 2 3 5 15 + +########################################################## +################ DEFINE VARIABLES HERE ################### +########################################################## + +TS=$(date +%m%d%y%H%M%S) # File time stamp +THISHOST=$(hostname) # Host name of this machine +LOGDIR=/usr/local/logs/script # Directory to hold the logs +LOGFILE=${THISHOST}.${EFF_USER}.$TS # Creates the name of the log file +touch $LOGDIR/$LOGFILE # Creates the actual file +TMOUT=300 # Set the user's shell timeout!!! +export TMOUT # Export the TMOUT variable +set -o vi 2>/dev/null # To recall previous commands + +# set path to include /usr/local/bin +echo $PATH|grep -q ':/usr/local/bin' || PATH=$PATH:/usr/local/bin + +# Set the command prompt to override the /.profile default prompt + +PS1="$THISHOST:b${EFF_USER}> " +export PS1 + +#################### RUN IT HERE ########################## + +chmod 666 ${LOGDIR}/${LOGFILE} # Set permission to read/write for the owner + +# To get the script sesssion to work we have to use the switch user (su) +# command with the -c flag, which means execute what follows. Sudo is also +# used just to ensure that root is executing the su command. We ARE executing +# now as root, because this script was started with sudo. If a non-configured +# sudo user tries to execute this command then it will fail unless sudo was +# used to execute this script as root. Notice we are executing the script +# command as "$EFF_USER". This variable is set at the top of the script. A +# value such as "EFF_USER=oracle" is expected. + +sudo su - $EFF_USER -c "script ${LOGDIR}/${LOGFILE}" + +chmod 400 ${LOGDIR}/${LOGFILE} # Set permission to read-only for the owner + +cleanup_exit # Execute the cleanup and exit function + diff --git a/chapter28/boracle.bash b/chapter28/boracle.bash new file mode 100755 index 0000000..4b0c38f --- /dev/null +++ b/chapter28/boracle.bash @@ -0,0 +1,123 @@ +#!/bin/bash +# +# SCRIPT: "Banybody" boracle.bash - This time +# +# AUTHOR: Randy Michael +# DATE: 05/08/2007 +# REV: 1.0.P +# PLATFOEM: Any UNIX +# +# PURPOSE: This shell script is used to capture all "$EFF_USER" access by +# capturing all of the terminal data in a log file using +# the script command. This shell script is executed from the +# command line using sudo (Super User Do). The log file +# is kept locally and e-mailed to a log file administrative +# user either locally or on a remote machine. Sudo must be +# configured for this shell script. Refer to your sudo notes. +# The effective user, currently oracle, can be changed by +# setting the "EFF_USER" variable to another user, and changing +# the name of the script. This is why the original name of the +# script is called "Banybody" +# +# ORIGINAL USAGE: sudo Banybody +# +# THIS TIME USAGE ==> USAGE: sudo boracle +# +# +# REV LIST: +# 5/10/2007: Modified the script to replace the hardcoded username +# with the variable $EFF_USER. This allows flexibility +# to add auditing of more accounts be just changing +# the EFF_USER variable and the script name. +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this shell script +# +# +################# DEFINE EFFECTIVE USER ################## + +# This EFF_USER is the user name you want to be to execute +# a shell in. An su command is used to switch to this user. + +EFF_USER=oracle + +############# DEFINE AUDIT LOG MANAGER ################### + +# This user receives all of the audit logs by e-mail. This +# Log Manager can have a local or remote e-mail address. You +# can add more than one e-mail address if you want by separating +# each address with a space. + +LOG_SERVER=yogi +LOG_MANAGER="logman@$LOG_SERVER" # List to email audit log + +# Set up the correct echo command usage. Some Linux machines +# may execute all scripts in Bash shell. + +case $(basename $SHELL) in +bash) alias echo="echo -e" + ;; +esac +########################################################## +################ DEFINE FUNCTIONS HERE ################### +########################################################## + +cleanup_exit () +{ +# This function is executed on any type of exit except of course +# a kill -9, which cannot be trapped. The script log file is +# emailed either locally or remotely, and the log file is +# compressed. The last "exit" is needed so the user does not +# have the ability to get to the command line without logging. + +if [[ -s ${LOGDIR}/${LOGFILE} ]] # Is it greater than zero bytes? +then + mailx -s "$TS - $LOGNAME Audit Report" $LOG_MANAGER < ${LOGDIR}/${LOGFILE} + nohup compress ${LOGDIR}/${LOGFILE} & +fi +} + +# Set a trap + +trap 'cleanup_exit' 1 2 3 5 15 + +########################################################## +################ DEFINE VARIABLES HERE ################### +########################################################## + +TS=$(date +%m%d%y%H%M%S) # File time stamp +THISHOST=$(hostname) # Host name of this machine +LOGDIR=/usr/local/logs/script # Directory to hold the logs +LOGFILE=${THISHOST}.${EFF_USER}.$TS # Creates the name of the log file +touch $LOGDIR/$LOGFILE # Creates the actual file +TMOUT=300 # Set the user's shell timeout!!! +export TMOUT # Export the TMOUT variable +set -o vi 2>/dev/null # To recall previous commands + +# set path to include /usr/local/bin +echo $PATH|grep -q ':/usr/local/bin' || PATH=$PATH:/usr/local/bin + +# Set the command prompt to override the /.profile default prompt + +PS1="$THISHOST:b${EFF_USER}> " +export PS1 + +#################### RUN IT HERE ########################## + +chmod 666 ${LOGDIR}/${LOGFILE} # Set permission to read/write for the owner + +# To get the script sesssion to work we have to use the switch user (su) +# command with the -c flag, which means execute what follows. Sudo is also +# used just to ensure that root is executing the su command. We ARE executing +# now as root, because this script was started with sudo. If a non-configured +# sudo user tries to execute this command then it will fail unless sudo was +# used to execute this script as root. Notice we are executing the script +# command as "$EFF_USER". This variable is set at the top of the script. A +# value such as "EFF_USER=oracle" is expected. + +sudo su - $EFF_USER -c "script ${LOGDIR}/${LOGFILE}" + +chmod 400 ${LOGDIR}/${LOGFILE} # Set permission to read-only for the owner + +cleanup_exit # Execute the cleanup and exit function + diff --git a/chapter28/broot b/chapter28/broot new file mode 100755 index 0000000..ef6f0ed --- /dev/null +++ b/chapter28/broot @@ -0,0 +1,94 @@ +#!/bin/ksh +# +# SCRIPT: broot +# +# AUTHOR: Randy Michael +# DATE: 05/08/2007 +# REV: 1.0.P +# PLATFORM: Any Unix +# +# PURPOSE: This shell script is used to monitor a login session by +# capturing all of the terminal data in a log file using +# the script command. This shell script name should be +# the last entry in the user.s $HOME/.profile. The log file +# is both kept locally and emailed to a log file +# administrative user either locally or on a remote machine. +# +# REV LIST: +# +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this shell script +# +############# DEFINE AUDIT LOG MANAGER ################### +# +# This user receives all of the audit logs by email. This +# Log Manager can have a local or remote email address. You +# can add more than one email address if you want by separating +# each address with a space. + +LOG_MANAGER="logman" # List to email audit log + +########################################################## +################ DEFINE FUNCTIONS HERE ################### +########################################################## + +cleanup_exit () +{ +# This function is executed on any type of exit except of course +# a kill -9, which cannot be trapped. The script log file is +# emailed either locally or remotely, and the log file is +# compressed. The last "exit" is needed so the user does not +# have the ability to get to the command line without logging. + +if [[ -s ${LOGDIR}/${LOGFILE} ]] +then + mailx -s "$TS - $LOGNAME Audit Report" $LOG_MANAGER \ + < ${LOGDIR}/${LOGFILE} + compress ${LOGDIR}/${LOGFILE} 2>/dev/null +fi +exit +} + +# Set a trap + +trap 'cleanup_exit' 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 26 + +########################################################## +################ DEFINE VARIABLES HERE ################### +########################################################## + +TS=$(date +%m%d%y%H%M%S) # File time stamp +THISHOST=$(hostname|cut -f1-2 -d.) # Host name of this machine +LOGDIR=/usr/local/logs/script # Directory to hold the logs +LOGFILE=${THISHOST}.${LOGNAME}.$TS # Creates the name of the log file +touch $LOGDIR/$LOGFILE # Creates the actual file +TMOUT=300 # Set the root shell timeout!!! +export TMOUT # Export the TMOUT variable + +# Run root's .profile if one exists + +if [[ -f $HOME/.profile ]] +then + . $HOME/.profile +fi + +# set path to include /usr/local/bin +echo $PATH | grep -q ':/usr/local/bin' || PATH=$PATH:/usr/local/bin + +# Set the command prompt to override the /.profile default prompt + +PS1="$THISHOST:broot> " +export PS1 + +#################### RUN IT HERE ########################## + +chmod 600 ${LOGDIR}/${LOGFILE} # Change permission to RW for the owner + +script ${LOGDIR}/${LOGFILE} # Start the script monitoring session + +chmod 400 ${LOGDIR}/${LOGFILE} # Set permission to read-only + # for the owner + +cleanup_exit # Execute the cleanup and exit function + diff --git a/chapter28/broot.bash b/chapter28/broot.bash new file mode 100755 index 0000000..1deaa73 --- /dev/null +++ b/chapter28/broot.bash @@ -0,0 +1,94 @@ +#!/bin/bash +# +# SCRIPT: broot.bash +# +# AUTHOR: Randy Michael +# DATE: 05/08/2007 +# REV: 1.0.P +# PLATFORM: Any Unix +# +# PURPOSE: This shell script is used to monitor a login session by +# capturing all of the terminal data in a log file using +# the script command. This shell script name should be +# the last entry in the user.s $HOME/.profile. The log file +# is both kept locally and emailed to a log file +# administrative user either locally or on a remote machine. +# +# REV LIST: +# +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this shell script +# +############# DEFINE AUDIT LOG MANAGER ################### +# +# This user receives all of the audit logs by email. This +# Log Manager can have a local or remote email address. You +# can add more than one email address if you want by separating +# each address with a space. + +LOG_MANAGER="logman" # List to email audit log + +########################################################## +################ DEFINE FUNCTIONS HERE ################### +########################################################## + +cleanup_exit () +{ +# This function is executed on any type of exit except of course +# a kill -9, which cannot be trapped. The script log file is +# emailed either locally or remotely, and the log file is +# compressed. The last "exit" is needed so the user does not +# have the ability to get to the command line without logging. + +if [[ -s ${LOGDIR}/${LOGFILE} ]] +then + mailx -s "$TS - $LOGNAME Audit Report" $LOG_MANAGER \ + < ${LOGDIR}/${LOGFILE} + compress ${LOGDIR}/${LOGFILE} 2>/dev/null +fi +exit +} + +# Set a trap + +trap 'cleanup_exit' 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 26 + +########################################################## +################ DEFINE VARIABLES HERE ################### +########################################################## + +TS=$(date +%m%d%y%H%M%S) # File time stamp +THISHOST=$(hostname|cut -f1-2 -d.) # Host name of this machine +LOGDIR=/usr/local/logs/script # Directory to hold the logs +LOGFILE=${THISHOST}.${LOGNAME}.$TS # Creates the name of the log file +touch $LOGDIR/$LOGFILE # Creates the actual file +TMOUT=300 # Set the root shell timeout!!! +export TMOUT # Export the TMOUT variable + +# Run root's .profile if one exists + +if [[ -f $HOME/.profile ]] +then + . $HOME/.profile +fi + +# set path to include /usr/local/bin +echo $PATH | grep -q ':/usr/local/bin' || PATH=$PATH:/usr/local/bin + +# Set the command prompt to override the /.profile default prompt + +PS1="$THISHOST:broot> " +export PS1 + +#################### RUN IT HERE ########################## + +chmod 600 ${LOGDIR}/${LOGFILE} # Change permission to RW for the owner + +script ${LOGDIR}/${LOGFILE} # Start the script monitoring session + +chmod 400 ${LOGDIR}/${LOGFILE} # Set permission to read-only + # for the owner + +cleanup_exit # Execute the cleanup and exit function + diff --git a/chapter28/function_cleanup_exit b/chapter28/function_cleanup_exit new file mode 100755 index 0000000..e5e51d5 --- /dev/null +++ b/chapter28/function_cleanup_exit @@ -0,0 +1,15 @@ +cleanup_exit () +{ +# This function is executed on any type of exit except of course +# a kill -9, which cannot be trapped. The script log file is +# emailed either locally or remotely, and the log file is +# compressed. The last "exit" is needed so the user does not +# have the ability to get to the command line without logging. + +if [[ -s ${LOGDIR}/${LOGFILE} ]] # Is it greater than zero bytes? +then + mailx -s "$TS - $LOGNAME Audit Report" $LOG_MANAGER < ${LOGDIR}/${LOGFILE} + nohup compress ${LOGDIR}/${LOGFILE} & +fi +} + diff --git a/chapter28/log_keystrokes.bash b/chapter28/log_keystrokes.bash new file mode 100755 index 0000000..0f15666 --- /dev/null +++ b/chapter28/log_keystrokes.bash @@ -0,0 +1,81 @@ +#!/bin/bash +# +# SCRIPT: log_keystrokes.bash +# +# AUTHOR: Randy Michael +# DATE: 05/08/2007 +# REV: 1.0.P +# PLATFOEM: Any UNIX +# +# PURPOSE: This shell script is used to monitor a login session by +# capturing all of the terminal data in a log file using +# the script command. This shell script name should be +# the last entry in the user's $HOME/.profile. The log file +# is both kept locally and e-mailed to a log file administrative +# user either locally or on a remote machine. +# +# REV LIST: +# +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this shell script +# +############# DEFINE AUDIT LOG MANAGER ################### + +# This user receives all of the audit logs by e-mail. This +# Log Manager can have a local or remote e-mail address. You +# can add more than one e-mail address if you want by separating +# each address with a space. + +LOG_MANAGER="logman" # List to e-mail audit log + +########################################################## +################ DEFINE FUNCTIONS HERE ################### +########################################################## + +cleanup_exit () +{ +# This function is executed on any type of exit except of course +# a kill -9, which cannot be trapped. The script log file is +# e-mailed either locally or remotely and the log file is +# compressed. The last "exit" is needed so the user does not +# have the ability to get to the command line without logging. + +if [[ -s ${LOGDIR}/${LOGFILE} ]] +then + mailx -s "$TS - $LOGNAME Audit Report" $LOG_MANAGER \ + < ${LOGDIR}/${LOGFILE} + compress ${LOGDIR}/${LOGFILE} 2>/dev/null +fi + +exit +} + +# Set a trap + +trap 'cleanup_exit' 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 26 + +########################################################## +################ DEFINE VARIABLES HERE ################### +########################################################## + +TS=$(date +%m%d%y%H%M%S) # File time stamp +THISHOST=$(hostname|cut -f1-2 -d.) # Host name of this machine +LOGDIR=/usr/local/logs/script # Directory to hold the logs +LOGFILE=${THISHOST}.${LOGNAME}.$TS # Creates the name of the log file +touch $LOGDIR/$LOGFILE # Creates the actual file +set -o vi 2>/dev/null # Previous commands recall + +# Set the command prompt +export PS1="[$LOGNAME:$THISHOST]@"'$PWD> ' + +#################### RUN IT HERE ########################## + +chmod 600 ${LOGDIR}/${LOGFILE} # Change permission to RW for the owner + +script ${LOGDIR}/${LOGFILE} # Start the script monitoring session + +chmod 400 ${LOGDIR}/${LOGFILE} # Set permission to read-only for the owner + +cleanup_exit # Execute the cleanup and exit function + diff --git a/chapter28/log_keystrokes.ksh b/chapter28/log_keystrokes.ksh new file mode 100755 index 0000000..17df4cc --- /dev/null +++ b/chapter28/log_keystrokes.ksh @@ -0,0 +1,81 @@ +#!/bin/ksh +# +# SCRIPT: log_keystrokes.ksh +# +# AUTHOR: Randy Michael +# DATE: 05/08/2002 +# REV: 1.0.P +# PLATFOEM: Any UNIX +# +# PURPOSE: This shell script is used to monitor a login session by +# capturing all of the terminal data in a log file using +# the script command. This shell script name should be +# the last entry in the user's $HOME/.profile. The log file +# is both kept locally and e-mailed to a log file administrative +# user either locally or on a remote machine. +# +# REV LIST: +# +# +# set -n # Uncomment to check syntax without any execution +# set -x # Uncomment to debug this shell script +# +############# DEFINE AUDIT LOG MANAGER ################### + +# This user receives all of the audit logs by e-mail. This +# Log Manager can have a local or remote e-mail address. You +# can add more than one e-mail address if you want by separating +# each address with a space. + +LOG_MANAGER="logman" # List to e-mail audit log + +########################################################## +################ DEFINE FUNCTIONS HERE ################### +########################################################## + +cleanup_exit () +{ +# This function is executed on any type of exit except of course +# a kill -9, which cannot be trapped. The script log file is +# e-mailed either locally or remotely and the log file is +# compressed. The last "exit" is needed so the user does not +# have the ability to get to the command line without logging. + +if [[ -s ${LOGDIR}/${LOGFILE} ]] +then + mailx -s "$TS - $LOGNAME Audit Report" $LOG_MANAGER \ + < ${LOGDIR}/${LOGFILE} + compress ${LOGDIR}/${LOGFILE} 2>/dev/null +fi + +exit +} + +# Set a trap + +trap 'cleanup_exit' 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 26 + +########################################################## +################ DEFINE VARIABLES HERE ################### +########################################################## + +TS=$(date +%m%d%y%H%M%S) # File time stamp +THISHOST=$(hostname|cut -f1-2 -d.) # Host name of this machine +LOGDIR=/usr/local/logs/script # Directory to hold the logs +LOGFILE=${THISHOST}.${LOGNAME}.$TS # Creates the name of the log file +touch $LOGDIR/$LOGFILE # Creates the actual file +set -o vi 2>/dev/null # Previous commands recall + +# Set the command prompt +export PS1="[$LOGNAME:$THISHOST]@"'$PWD> ' + +#################### RUN IT HERE ########################## + +chmod 600 ${LOGDIR}/${LOGFILE} # Change permission to RW for the owner + +script ${LOGDIR}/${LOGFILE} # Start the script monitoring session + +chmod 400 ${LOGDIR}/${LOGFILE} # Set permission to read-only for the owner + +cleanup_exit # Execute the cleanup and exit function + diff --git a/chapter28/nohup.out b/chapter28/nohup.out new file mode 100755 index 0000000..e69de29 diff --git a/chapter4/function_dots b/chapter4/function_dots new file mode 100755 index 0000000..fd3a4d3 --- /dev/null +++ b/chapter4/function_dots @@ -0,0 +1,10 @@ +function dots +{ +SEC=$1 # How many seconds between dots? + +while true +do + sleep $SEC + echo ".\c" +done +} diff --git a/chapter4/function_elapsed_time b/chapter4/function_elapsed_time new file mode 100755 index 0000000..14d5ef0 --- /dev/null +++ b/chapter4/function_elapsed_time @@ -0,0 +1,10 @@ +elasped_time () +{ +SEC=$1 +(( SEC < 60 )) && $ECHO "[Elasped time: $SEC seconds]\c" + +(( SEC >= 60 && SEC < 3600 )) && $ECHO "[Elasped time: $(( SEC / 60 )) min $(( SEC % 60 )) sec]\c" + +(( SEC > 3600 )) && $ECHO "[Elasped time: $(( SEC / 3600 )) hr $(( (SEC % 3600) / 60 )) min $(( (SEC % 3600) % 60 )) sec]\c" +} + diff --git a/chapter4/function_rotate b/chapter4/function_rotate new file mode 100755 index 0000000..4ed88f6 --- /dev/null +++ b/chapter4/function_rotate @@ -0,0 +1,46 @@ +function rotate +{ +# PURPOSE: This function is used to give the end user +# some feedback that "something" is running. It gives +# a line rotating in a circle. This function is started +# as a background process. Assign its PID to a variable +# using +# rotate & # To start +# ROTATE_PID=$! # Get the PID of the last +# # background job +# +# At the end of execution just break out by +# killing the $ROTATE_PID process. We also need +# to do a quick "cleanup" of the leftover line of +# rotate output. +# +# FROM THE SCRIPT: +# kill -9 $ROTATE_PID +# echo "\b\b " + +INTERVAL=1 # Sleep time between rotation intervals +RCOUNT="0" # For each RCOUNT the line rotates 1/8 + # cycle + +while : # Loop forever...until this function is killed +do + (( RCOUNT = RCOUNT + 1 )) # Increment the RCOUNT + + case $RCOUNT in + 1) echo .-."\b\c" + sleep $INTERVAL + ;; + 2) echo .\\."\b\c" + sleep $INTERVAL + ;; + 3) echo "|\b\c" + sleep $INTERVAL + ;; + 4) echo "/\b\c" + sleep $INTERVAL + ;; + *) RCOUNT="0" # Reset the RCOUNT to "0", zero. + ;; + esac +done +} # End of Function - rotate diff --git a/chapter4/timing_test_function.bash b/chapter4/timing_test_function.bash new file mode 100755 index 0000000..aa9d034 --- /dev/null +++ b/chapter4/timing_test_function.bash @@ -0,0 +1,36 @@ +#!/bin/bash + +# SCRIPT: timing_test_function.bash +# AUTHOR: Randy Michael + +######## DEFINE VARIABLES HERE ######## + +# Ready script for ksh or bash +ECHO=echo +[[ $(basename $SHELL) == bash ]] && ECHO="echo -e" + + +elasped_time () +{ +SEC=$1 +(( SEC < 60 )) && $ECHO "[Elasped time: $SEC seconds]\c" + +(( SEC >= 60 && SEC < 3600 )) && $ECHO "[Elasped time: $(( SEC / 60 )) min $(( SEC % 60 )) sec]\c" + +(( SEC > 3600 )) && $ECHO "[Elasped time: $(( SEC / 3600 )) hr $(( (SEC % 3600) / 60 )) min $(( (SEC % 3600) % 60 )) sec]\c" +} + +###### BEGINNING OF MAIN + +SECONDS=15 +elasped_time $SECONDS +$ECHO +SECONDS=60 +elasped_time $SECONDS +$ECHO +SECONDS=3844 +elasped_time $SECONDS +$ECHO +sleep 10 +elasped_time $SECONDS +$ECHO diff --git a/chapter5/function_merge_fixed_length_records b/chapter5/function_merge_fixed_length_records new file mode 100755 index 0000000..73593dd --- /dev/null +++ b/chapter5/function_merge_fixed_length_records @@ -0,0 +1,11 @@ +function merge_fixed_length_records +{ +# set -x +while read RECORDFILENAME +do + + sed s/$/$(basename $RECORDFILENAME 2>/dev/null)/g $RECORDFILENAME >> $MERGERECORDFILE + +done < $RECORDFILELIST +} + diff --git a/chapter5/function_merge_variable_length_records b/chapter5/function_merge_variable_length_records new file mode 100755 index 0000000..454e108 --- /dev/null +++ b/chapter5/function_merge_variable_length_records @@ -0,0 +1,11 @@ +function merge_variable_length_records +{ +# set -x +while read RECORDFILENAME +do + +sed s/$/${FD}$(basename $RECORDFILENAME 2>/dev/null)/g $RECORDFILENAME >> $MERGERECORDFILE + +done < $RECORDFILELIST +} + diff --git a/chapter5/function_parse_fixed_length_records b/chapter5/function_parse_fixed_length_records new file mode 100755 index 0000000..9da1874 --- /dev/null +++ b/chapter5/function_parse_fixed_length_records @@ -0,0 +1,43 @@ +function parse_fixed_length_records +{ +# set -x +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +while read RECORD +do + # On each loop iteration extract the data fields + # from the record as we process the record file + # line by line + BRANCH=$(echo "$RECORD" | cut -c1-6) + ACCOUNT=$(echo "$RECORD" | cut -c7-25) + NAME=$(echo "$RECORD" | cut -c26-45) + TOTAL=$(echo "$RECORD" | cut -c46-70) + DUEDATE=$(echo "$RECORD" | cut -c71-78) + RECFILE=$(echo "$RECORD" | cut -c79-) + + # Perform some action on the data + + process_fixedlength_data_new_duedate $BRANCH $ACCOUNT $NAME \ + $TOTAL $DUEDATE $RECFILE $NEW_DATEDUE + if (( $? != 0 )) + then + # Note that $LOGFILE is a global variable + echo "Record Error: $RECORD" | tee -a $LOGFILE + fi +done < $MERGERECORDFILE + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- +} + diff --git a/chapter5/function_parse_variable_length_records b/chapter5/function_parse_variable_length_records new file mode 100755 index 0000000..5f25364 --- /dev/null +++ b/chapter5/function_parse_variable_length_records @@ -0,0 +1,42 @@ +function parse_variable_length_records +{ +# set -x +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +while read RECORD +do + # On each loop iteration extract the data fields + # from the record as we process the record file + # line by line + + echo $RECORD | awk -F : '{print $1, $2, $3, $4, $5, $6}' \ + | while read BRANCH ACCOUNT NAME TOTAL DATEDUE RECFILE + do + # Perform some action on the data + + process_variablelength_data_new_duedate $BRANCH $ACCOUNT $NAME \ + $TOTAL $DATEDUE $RECFILE $NEW_DATEDUE + if (( $? != 0 )) + then + # Note that $LOGFILE is a global variable + echo "Record Error: $RECORD" | tee -a $LOGFILE + fi + done + +done < $MERGERECORDFILE + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- +} + diff --git a/chapter5/parse_record_files.bash b/chapter5/parse_record_files.bash new file mode 100755 index 0000000..7747390 --- /dev/null +++ b/chapter5/parse_record_files.bash @@ -0,0 +1,242 @@ +#!/bin/bash +# +# SCRIPT: parse_record_files.bash +# AUTHOR: Randy Michael +# DATE: 12/7/2007 +# REV: 1.0 +# PURPOSE: This script is used to parse both +# fixed-length and variable-length record files. +# Before we parse the records we first merge the +# files into a single file for batch processing. +# +# set -n # Uncomment to check script syntax +# # without any execution +# set -x # Uncomment to debug +# +# REV LIST: +# +# Revised by: +# Revision date: +# Revision: +# +# +# +########################################## +# VERIFY INPUT +########################################## + +if (( $# != 1 )) +then + echo -e "\nUSAGE: $(basename $0) -f|-v" + echo -e "\nWhere -f = fixed-length records" + echo -e "and -v = variable-length records\n" + exit 1 +else + case $1 in + -f) RECORD_TYPE=fixed + ;; + -v) RECORD_TYPE=variable + ;; + *) echo -e "\nUSAGE: $(basename $0) -f|-v" + echo -e "\nWhere -f = fixed-length records" + echo -e "and -v = variable-length records\n" + exit 1 + ;; + esac +fi + +########################################## +# DEFINE FILES AND VARIABLES HERE +########################################## + +DATADIR=/data # This variable defines the directory to use for data + +if [ $RECORD_TYPE = fixed ] +then + MERGERECORDFILE=${DATADIR}/mergedrecords_fixed.$(date +%m%d%y) + >$MERGERECORDFILE # Zero out the file to start + RECORDFILELIST=${DATADIR}/branch_records_fixed.lst + OUTFILE=${DATADIR}/post_processing_fixed_records.dat + >$OUTFILE # Zero out the file to start +else + MERGERECORDFILE=${DATADIR}/mergedrecords_variable.$(date +%m%d%y) + >$MERGERECORDFILE # Zero out the file to start + RECORDFILELIST=${DATADIR}/branch_records_variable.lst + OUTFILE=${DATADIR}/post_processing_variable_records.dat + >$OUTFILE # Zero out the file to start +fi + +# Test for Solaris to alias awk to nawk + +case $(uname) in +SunOS) alias awk=nawk + ;; +esac + +FD=: # This variable defines the field delimiter for fixed-length records + +NEW_DATEDUE=01312008 + +########################################## + +function process_fixedlength_data_new_duedate +{ +# set -x +# Local positional variables +branch=$1 +account=$2 +name=$3 +total=$4 +datedue=$5 +recfile=$6 +new_datedue=$7 + +echo "${branch}${account}${name}${total}${new_datedue}${recfile}" \ + >> $OUTFILE +} + +########################################## + +function process_variablelength_data_new_duedate +{ +# set -x +# Local positional variables +branch=$1 +account=$2 +name=$3 +total=$4 +datedue=$5 +recfile=$6 +new_datedue=$7 + +echo "${branch}${FD}${account}${FD}${name}${FD}${total}\ +${FD}${new_datedue}${FD}${recfile}" >> $OUTFILE +} + +########################################## + +function merge_fixed_length_records +{ +# set -x +while read RECORDFILENAME +do + + sed s/$/$(basename $RECORDFILENAME 2>/dev/null)/g $RECORDFILENAME >> $MERGERECORDFILE + +done < $RECORDFILELIST +} + +########################################## + +function merge_variable_length_records +{ +# set -x +while read RECORDFILENAME +do + +sed s/$/${FD}$(basename $RECORDFILENAME 2>/dev/null)/g $RECORDFILENAME >> $MERGERECORDFILE + +done < $RECORDFILELIST +} + +########################################## + +function parse_fixed_length_records +{ +# set -x +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +while read RECORD +do + # On each loop iteration extract the data fields + # from the record as we process the record file + # line by line + BRANCH=$(echo "$RECORD" | cut -c1-6) + ACCOUNT=$(echo "$RECORD" | cut -c7-25) + NAME=$(echo "$RECORD" | cut -c26-45) + TOTAL=$(echo "$RECORD" | cut -c46-70) + DUEDATE=$(echo "$RECORD" | cut -c71-78) + RECFILE=$(echo "$RECORD" | cut -c79-) + + # Perform some action on the data + + process_fixedlength_data_new_duedate $BRANCH $ACCOUNT $NAME \ + $TOTAL $DUEDATE $RECFILE $NEW_DATEDUE + if (( $? != 0 )) + then + # Note that $LOGFILE is a global variable + echo "Record Error: $RECORD" | tee -a $LOGFILE + fi +done < $MERGERECORDFILE + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- +} + +########################################## + +function parse_variable_length_records +{ +# set -x +# Zero out the $OUTFILE + +>$OUTFILE + +# Associate standard output with file descriptor 4 +# and redirect standard output to $OUTFILE + +exec 4<&1 +exec 1> $OUTFILE + +while read RECORD +do + # On each loop iteration extract the data fields + # from the record as we process the record file + # line by line + + echo $RECORD | awk -F : '{print $1, $2, $3, $4, $5, $6}' \ + | while read BRANCH ACCOUNT NAME TOTAL DATEDUE RECFILE + do + # Perform some action on the data + + process_variablelength_data_new_duedate $BRANCH $ACCOUNT $NAME \ + $TOTAL $DATEDUE $RECFILE $NEW_DATEDUE + if (( $? != 0 )) + then + # Note that $LOGFILE is a global variable + echo "Record Error: $RECORD" | tee -a $LOGFILE + fi + done + +done < $MERGERECORDFILE + +# Restore standard output and close file +# descriptor 4 + +exec 1<&4 +exec 4>&- +} + +########################################## +# BEGINNING OF MAIN +########################################## + +case $RECORD_TYPE in +fixed) merge_fixed_length_records + parse_fixed_length_records + ;; +variable) merge_variable_length_records + parse_variable_length_records + ;; +esac diff --git a/chapter6/ftp-ls-getfile.exp b/chapter6/ftp-ls-getfile.exp new file mode 100755 index 0000000..38ab96e --- /dev/null +++ b/chapter6/ftp-ls-getfile.exp @@ -0,0 +1,100 @@ +#!/usr/local/bin/expect -f +# +# This Expect script was generated by autoexpect on Fri Nov 16 12:29:38 2007 +# Expect and autoexpect were both written by Don Libes, NIST. +# +# Note that autoexpect does not guarantee a working script. It +# necessarily has to guess about certain things. Two reasons a script +# might fail are: +# +# 1) timing - A surprising number of programs (rn, ksh, zsh, telnet, +# etc.) and devices discard or ignore keystrokes that arrive "too +# quickly" after prompts. If you find your new script hanging up at +# one spot, try adding a short sleep just before the previous send. +# Setting "force_conservative" to 1 (see below) makes Expect do this +# automatically - pausing briefly before sending each character. This +# pacifies every program I know of. The -c flag makes the script do +# this in the first place. The -C flag allows you to define a +# character to toggle this mode off and on. + +set force_conservative 0 ;# set to 1 to force conservative mode even if + ;# script wasn't run conservatively originally +if {$force_conservative} { + set send_slow {1 .1} + proc send {ignore arg} { + sleep .1 + exp_send -s -- $arg + } +} + +# +# 2) differing output - Some programs produce different output each time +# they run. The "date" command is an obvious example. Another is +# ftp, if it produces throughput statistics at the end of a file +# transfer. If this causes a problem, delete these patterns or replace +# them with wildcards. An alternative is to use the -p flag (for +# "prompt") which makes Expect only look for the last line of output +# (i.e., the prompt). The -P flag allows you to define a character to +# toggle this mode off and on. +# +# Read the man page for more info. +# +# -Don + + +set timeout -1 +spawn $env(SHELL) +match_max 100000 +expect -exact "\]0;root@yogi:/scripts\[root@yogi scripts\]# " +send -- "ftp booboo\r" +expect -exact "ftp booboo\r +Connected to booboo.\r +220 (vsFTPd 1.2.1)\r +530 Please login with USER and PASS.\r +530 Please login with USER and PASS.\r +KERBEROS_V4 rejected as an authentication type\r +Name (booboo:root): " +send -- "ftpadmin\r" +expect -exact "ftpadmin\r +331 Please specify the password.\r +Password:" +send -- "abc1234\r" +expect -exact "\r +230 Login successful.\r +Remote system type is UNIX.\r +Using binary mode to transfer files.\r +ftp> " +send -- "ls\r" +expect -exact "ls\r +227 Entering Passive Mode (192,167,37,43,182,234)\r +150 Here comes the directory listing.\r +-rw-r--r-- 1 4465 4465 29187 Sep 06 21:52 Linux_booboo_cronlistings.txt\r +-rwxr-xr-x 1 4465 4465 1238 Sep 06 19:51 cron_capture\r +-rwxr-xr-x 1 4465 4465 1230 Sep 06 20:23 cron_capture-LINUX\r +-rw-r--r-- 1 0 0 84 Apr 03 2007 drinfo.sh\r +-rw-rw-r-- 1 4465 4465 368535 Sep 07 20:40 get_AIX_sysinfo.log\r +-rw-rw-r-- 1 4465 4465 205 Sep 07 15:48 get_dr_info.ksh\r +-rw-rw-r-- 1 4465 4465 10933 Sep 07 20:35 get_sysinfo2.log\r +-rw-r--r-- 1 0 0 666 Apr 03 2007 getall.sh\r +-rw-r--r-- 1 0 0 256 Apr 03 2007 imp.sh\r +-rw-r--r-- 1 0 0 323 Apr 03 2007 pb_getnet.sh\r +-rw-r--r-- 1 0 0 167 Apr 03 2007 pb_getvg.sh\r +-rw-r--r-- 1 0 0 618 Apr 03 2007 putall.sh\r +-rw-r--r-- 1 0 0 10240 Sep 07 15:50 scripts.tar\r +-rw-r--r-- 1 0 0 32 Apr 03 2007 um.sh\r +226 Directory send OK.\r +ftp> " +send -- "get getall.sh\r" +expect -exact "get getall.sh\r +local: getall.sh remote: getall.sh\r +227 Entering Passive Mode (192,168,37,43,52,152)\r +150 Opening BINARY mode data connection for getall.sh (666 bytes).\r +226 File send OK.\r +666 bytes received in 0.064 seconds (10 Kbytes/s)\r +ftp> " +send -- "bye\r" +expect -exact "bye\r +221 Goodbye.\r +\]0;root@yogi:/scripts\[root@yogi scripts\]# " +send -- "" +expect eof diff --git a/chapter6/function_post_event b/chapter6/function_post_event new file mode 100755 index 0000000..701a6e5 --- /dev/null +++ b/chapter6/function_post_event @@ -0,0 +1,10 @@ +post_event () +{ +# Add anything that you want to execute in this function. You can +# hardcode the tasks in this function or create an external shell +# script and execute the external function here. + +: # No-Op: The colon (:) is a No-Op character. It does nothing and + # always produces a 0, zero, return code. +} + diff --git a/chapter6/function_pre_event b/chapter6/function_pre_event new file mode 100755 index 0000000..e7a24a3 --- /dev/null +++ b/chapter6/function_pre_event @@ -0,0 +1,10 @@ +pre_event () +{ +# Add anything that you want to execute in this function. You can +# hardcode the tasks in this function or create an external shell +# script and execute the external function here. + +: # No-Op: The colon (:) is a No-Op character. It does nothing and + # always produces a 0, zero, return code. +} + diff --git a/chapter6/get_ftp_files.ksh b/chapter6/get_ftp_files.ksh new file mode 100755 index 0000000..0b827b0 --- /dev/null +++ b/chapter6/get_ftp_files.ksh @@ -0,0 +1,106 @@ +#!/bin/ksh +# +# SCRIPT: get_ftp_files.ksh +# AUTHOR: Randy Michael +# DATE: July 15, 2007 +# REV: 1.1.P +# +# PLATFORM: Not Platform Dependent +# +# PURPOSE: This shell script uses FTP to get a one or more remote +# files from a remote machine. +# +# set -n # Uncomment to check the script syntax without any execution +# set -x # Uncomment to debug this shell script +# +################################################################### +################## DEFINE VARIABLES HERE ########################## +################################################################### + +REMOTEFILES=$1 + +THISSCRIPT=$(basename $0) +RNODE="wilma" +USER="randy" +UPASSWD="mypassword" +LOCALDIR="/scripts/download" +REMOTEDIR="/scripts" + +# Set up the correct echo command usage. Many Linux +# distributions will execute in Bash even if the +# script specifies Korn shell. Bash shell requires +# we use echo -e when we use \n, \c, etc. + +case $(basename $SHELL) in +bash) alias echo="echo -e" + ;; +esac + +################################################################### +################## DEFINE FUNCTIONS HERE ########################## +################################################################### + +pre_event () +{ +# Add anything that you want to execute in this function. You can +# hardcode the tasks in this function or create an external shell +# script and execute the external function here. + +: # No-Op: The colon (:) is a No-Op character. It does nothing and + # always produces a 0, zero, return code. +} + +################################################################### + +post_event () +{ +# Add anything that you want to execute in this function. You can +# hardcode the tasks in this function or create an external shell +# script and execute the external function here. + +: # No-Op: The colon (:) is a No-Op character. It does nothing and + # always produces a 0, zero, return code. +} + +################################################################### + +usage () +{ +echo "\nUSAGE: $THISSCRIPT \"One or More Filenames to Download\" \n" +exit 1 +} + +################################################################### + +usage_error () +{ +echo "\nERROR: This shell script requires a list of one or more + files to download from the remote site.\n" + +usage +} + +################################################################### +##################### BEGINNING OF MAIN ########################### +################################################################### + +# Test for a single command-line argument. This should contain a list +# of one or more files. + +(($# != 1)) && usage_error + +pre_event + +ftp -i -v -n $RNODE < $DIRLISTFILE + +################################################################### +##################### BEGINNING OF MAIN ########################### +################################################################### + +ftp -i -v -n $RNODE < $DIRLISTFILE + +################################################################### +##################### BEGINNING OF MAIN ########################### +################################################################### + +# Get a password + +. /usr/sbin/setlink.ksh + +ftp -i -v -n $RNODE <= 60 && SEC < 3600 )) && echo "[Elapsed time: $(( SEC / 60 )) \ +min $(( SEC % 60 )) sec]\c" + +(( SEC > 3600 )) && echo "[Elapsed time: $(( SEC / 3600 )) hr $(( (SEC % 3600) / 60 )) \ +min $(( (SEC % 3600) % 60 )) sec]\c" +} + diff --git a/chapter7/function_ready_to_run b/chapter7/function_ready_to_run new file mode 100755 index 0000000..977e9d4 --- /dev/null +++ b/chapter7/function_ready_to_run @@ -0,0 +1,18 @@ +ready_to_run () +{ +# set -x +# This function looks for a file on the system +# defined by the $READYTORUN_FILE variable. The +# presents presence of this file indicates we are ready +# to run this script. This file will contain a +# number 1 or 2 identifying the day we are +# working with. + +if [ -r ${READYTORUN_FILE} ] +then + cat ${READYTORUN_FILE} +else + echo "NOT_READY" +fi +} + diff --git a/chapter7/function_verify_copy b/chapter7/function_verify_copy new file mode 100755 index 0000000..9124a55 --- /dev/null +++ b/chapter7/function_verify_copy @@ -0,0 +1,68 @@ +verify_copy () +{ +#set -x + +MYLOGFILE=${WORK_DIR}/verify_rsync_copy_day${DAY}.log +>$MYLOGFILE +ERROR=0 + +# Enclose this loop so we can redirect output to the log file +# with one assignment at the bottom of the function +{ +# Put a header for the verification log file + +echo "\nRsync copy verification between $THIS_HOST and machines $MACHINE_LIST\n\n" >$MYLOGFILE + + +for M in $MACHINE_LIST +do + for LOC_MP in $(df | grep oradata_dm_[0-2][0-9] | awk '{print $7}') + do + LS_FILES=$(find $LOC_MP -type f) + for FL in $LS_FILES + do + LOC_FS=$(ls -l $FL | awk '{print $5}' 2>&1) + + # This sed statement changes the "m" to $DAY + REM_FL=$(echo $FL | sed s/oradata_dm_/oradata_d${DAY}_/g) + + REM_FS=$(rsh $M ls -l $REM_FL | awk '{print $5}' 2>&1) + echo "Checking File: $FL" + echo "Local $THIS_HOST size:\t$LOC_FS" + echo "Checking Remote File: $REM_FL" + echo "$M size:\t$REM_FS" + if [ "$LOC_FS" -ne "$REM_FS" ] + then + echo "ERROR: File size mismatch between $THIS_HOST and $M" + echo "File is: $FL" + ERROR=1 + fi + done + done +done + +if (( ERROR != 0 )) +then + # Record the failure in the log file + + echo "\n\nRSYNC ERROR: $THIS_HOST Rsync copy failed...file size \ +mismatch...\n\n" | tee -a $MYLOGFILE + + # Send email notification with file size log + + mailx -r "$EMAIL_FROM" -s "RSYNC ERROR: $THIS_HOST Rsync copy failed\ +...file size mismatch -- log attached" data_support < $MYLOGFILE + + echo "\nERROR: Rsync copy Failed!" + echo "\n\nCheck log file: $MYLOGFILE\n\n" + echo "\n...Exiting...\n" + cleanup_exit 3 + return 3 +else + echo "\nSUCCESS: Rsync copy completed successfully..." + echo "\nAll file sizes match...\n" +fi +} | tee -a $MYLOGFILE + +} + diff --git a/chapter7/generic_DIR_rsync_copy.bash b/chapter7/generic_DIR_rsync_copy.bash new file mode 100755 index 0000000..07d99b2 --- /dev/null +++ b/chapter7/generic_DIR_rsync_copy.bash @@ -0,0 +1,344 @@ +#!/bin/bash +# +# SCRIPT: generic_DIR_rsync_copy.bash +# AUTHOR: Randy Michael +# DATE: 1/14/2007 +# REV: 1.0 +# PLATFORM: Not platform dependent +# +# REQUIRED INSTALLATION: rsync must be +# installed on the source and +# all target systems +# +# REQUIRED FILE FOR OPERATION: +# The file pointed to by the +# $DIR_LIST_FILE variable refers +# to the required file listing the +# top level directory structures +# that rsync is to replicate. The +# DIR_LIST_FILE is defined in the +# variables declarations section +# of this shell script +# +# PURPOSE: This shell script is used to replicate +# directory structures to one or more remote +# machines using rsync in archive and +# compressed mode. This script requires +# a file referenced by the variable +# DIR_LIST_FILE that lists the top level +# of each of the local directory structures +# that we want to replicate to one or more +# remote machines +# +# set -x # Uncomment to debug this script +# +# set -n # Uncomment to check script syntax +# # without execution. Remember to put +# # the comment back into the script or it +# # will never execute. +# +####################################### +# DEFINE FILES AND VARIABLES HERE +####################################### + +# Define the target machines to copy data to. +# To specify more than one host enclose the +# hostnames in double quotes and put at least +# one space between each hostname +# +# EXAMPLE: MACHINE_LIST="fred yogi booboo" + +MACHINE_LIST="fred" + +# The DIR_LIST_FILE variable define the shell script +# required file listing the directory structures +# to replicate with rsync. + +DIR_LIST_FILE="rsync_directory_list.lst" + +# Define the directory containing all of the +# output files this shell script will produce + +WORK_DIR=/usr/local/bin + +# Define the rsync script log file + +LOGFILE="generic_DIR_rsync_copy.log" + +# Query the system for the hostname + +THIS_HOST=$(hostname) + +# Query the system for the UNIX flavor + +OS=$(uname) + +####################################### +# DEFINE FUNCTIONS HERE +####################################### + +elapsed_time () +{ +SEC=$1 +(( SEC < 60 )) && echo -e "[Elapsed time: $SEC seconds]\c" + +(( SEC >= 60 && SEC < 3600 )) && echo -e "[Elapsed time: \ +$(( SEC / 60 )) min $(( SEC % 60 )) sec]\c" + +(( SEC > 3600 )) && echo -e "[Elapsed time: $(( SEC / 3600 )) \ +hr $(( (SEC % 3600) / 60 )) min $(( (SEC % 3600) % 60 )) \ +sec]\c" +} + +####################################### + +verify_copy () +{ +# set -x + +MYLOGFILE=${WORK_DIR}/verify_rsync_copy.log +>$MYLOGFILE +ERROR=0 + +# Enclose this loop so we can redirect output to the log file +# with one assignment at the bottom of the function +{ +# Put a header for the verification log file + +echo -e "\nRsync copy verification starting between $THIS_HOST \ +and machine(s) $MACHINE_LIST\n\n" >$MYLOGFILE + +for M in $MACHINE_LIST +do + for DS in $(cat $DIR_LIST_FILE) + do + LS_FILES=$(find $DS -type f) + for FL in $LS_FILES + do + LOC_FS=$(ls -l $FL | awk '{print $5}' 2>&1) + REM_FS=$(ssh $M ls -l $FL | awk '{print $5}' 2>&1) + echo "Checking File: $FL" + echo -e "Local $THIS_HOST size:\t$LOC_FS" + echo -e "Remote $M size:\t$REM_FS" + if [ "$LOC_FS" -ne "$REM_FS" ] + then + echo "ERROR: File size mismatch between $THIS_HOST and $M" + echo "File is: $FL" + ERROR=1 + fi + done + done +done + +if (( ERROR != 0 )) +then + # Record the failure in the log file + + echo -e "\n\nRSYNC ERROR: $THIS_HOST Rsync copy failed...file\ + size mismatch...\n\n" | tee -a $MYLOGFILE + + echo -e "\nERROR: Rsync copy Failed!" + echo -e "\n\nCheck log file: $MYLOGFILE\n\n" + echo -e "\n...Exiting...\n" + + # Send email notification with file size log + +# mailx -r from_someone@somewhere.??? -s "Rsync copy verification \ +#failed $THIS_HOST -- File size mismatch" \ +# somebody@fsf.com < $MYLOGFILE + + sleep 2 # Give a couple of seconds to send the email + + exit 3 +else + echo -e "\nSUCCESS: Rsync copy completed successfully..." + echo -e "\nAll file sizes match...\n" +fi +} | tee -a $MYLOGFILE + +} + +####################################### +# BEGINNING OF MAIN +####################################### + +# We enclose the entire MAIN in curly craces +# so that all output of the script is logged +# in the $LOGFILE with a single output +# redirection + +{ + +# Save the last logfile with a .yesterday filename extension + +cp -f $LOGFILE ${LOGFILE}.yesterday 2>/dev/null + +# Zero out the $LOGFILE to start a new file + +>$LOGFILE + +# Start all of the rsync copy sessions at once by looping +# through each of the mount points and issuing an rsync +# command in the background, saving the PID of the +# background process, and incrementing a counter. + +echo -e "\nStarting a bunch of rsync sessions...$(date)\n" + +# Initialize the rsync session counter, TOTAL +# to zero + +TOTAL=0 + +# Loop through each machine in the $MACHINE_LIST + +for M in $MACHINE_LIST +do + # Loop through all of the directory structures + # listed in the $DIR_LIST_FILE and start an rsync + # session in the background for each directory + + for DS in $(cat $DIR_LIST_FILE) + do + # Ensure each directory structure has a trailing + # forward slash to ensure an extra directory is + # not created on the target + + if ! $(echo "$DS" | grep -q '/$') + then + # If the directory structure does not + # have a trailing forward slash, add it + + DS="${DS}/" + fi + + # Start a timed rsync session in the background + + time rsync -avz ${DS} ${M}:${DS} 2>&1 & + + # Keep a running total of the number of rsync + # sessions started + (( TOTAL = TOTAL + 1 )) + done +done + +# Sleep a 10 seconds before monitoring processes + +sleep 10 + +# Query the process table for the number of rsync sessions +# that continue executing and store that value in the +# REM_SESSIONS variable + +# Create a list of directory structures for an egrep list + +EGREP_LIST= # Initialize to null + +while read DS +do + if [ -z "$EGREP_LIST" ] + then + EGREP_LIST="$DS" + else + EGREP_LIST="|$DS" + fi +done < $DIR_LIST_FILE + +REM_SESSIONS=$(ps x | grep "rsync -avz" | egrep "$EGREP_LIST" \ + | grep -v grep | awk '{print $1}' | wc -l) + +# Give some feedback to the end user. + +if (( REM_SESSIONS > 0 )) +then + echo -e "\n$REM_SESSIONS of $TOTAL rsync copy sessions require further updating..." + echo -e "\nProcessing rsync copies from $THIS_HOST to both $MACHINE_LIST machines" + echo -e "\nPlease be patient, this process may a very long time...\n" + echo -e "Rsync is running [Start time: $(date)]\c" +else + echo -e "\nAll files appear to be in sync...verifying file sizes...Please wait...\n" +fi + +# While the rsync processes are executing this loop +# will place a . (dot) every 60 seconds as feedback +# to the end user that the background rsync copy +# processes are still executing. When the remaining +# rsync sessions are less than the total number of +# sessions this loop will give feedback as to the +# number of remaining rsync session, as well as +# the elapsed time of the procesing, every 5 minutes + +# Set the shell variable SECONDS to 10 to make up for the +# time we slept while the rsync sessions started + +SECONDS=10 + +# Initialize the minute counter to zero minutes + +MIN_COUNTER=0 + +# Loop until all of the rsync sessions have completed locally + +until (( REM_SESSIONS == 0 )) +do + sleep 60 + echo -e ".\c" + REM_SESSIONS=$(ps x | grep "rsync -avz" | egrep "$EGREP_LIST" \ + | grep -v grep | awk '{print $1}' | wc -l) + if (( REM_SESSIONS < TOTAL )) + then + (( MIN_COUNTER = MIN_COUNTER + 1 )) + if (( MIN_COUNTER >= $(( TOTAL / 2 )) )) + then + MIN_COUNTER=0 + echo -e "\n$REM_SESSIONS of $TOTAL rsync sessions \ +remaining $(elapsed_time $SECONDS)\c" + if (( REM_SESSIONS <= $(( TOTAL / 4 )) )) + then + echo -e "\nRemaining rsync sessions include:\n" + ps aux | grep "rsync -avz" | egrep "$EGREP_LIST" \ + | grep -v grep + echo + fi + fi + fi +done + +echo -e "\n...Local rsync processing completed on ${THIS_HOST}...$(date)" + +echo -e "\n...Checking remote target machine(s): ${MACHINE_LIST}..." + +# Just because the local rsync processes have completed does +# not mean that the remote rsync processes have completed +# execution. This loop verifies that all of the remote +# rsync processes completed execution. The end user will +# receive feedback if any of the remote processes are running. + +for M in $MACHINE_LIST +do + for DS in $(cat $DIR_LIST_FILE) + do + RPID=$(ssh $M ps x | grep rsync | grep $DS | grep -v grep | awk '{print $1}') + until [ -z "$RPID" ] + do + echo "rsync is processing ${MP} on ${M}...sleeping one minute..." + sleep 60 + RPID=$(ssh $M ps x | grep rsync | grep $DS | grep -v grep \ + | awk '{print $1}') + done + done +done + +echo -e "\n...Remote rsync processing completed $(date)\n" + +# Verify the copy process + +verify_copy + +echo -e "\nRsync copy completed $(date)" +echo -e "\nElapsed time: $(elapsed_time $SECONDS)\n" + +} 2>&1 | tee -a $LOGFILE + +############################################### +# END OF SCRIPT +############################################### diff --git a/chapter7/generic_FS_rsync_copy.bash b/chapter7/generic_FS_rsync_copy.bash new file mode 100755 index 0000000..4edbd66 --- /dev/null +++ b/chapter7/generic_FS_rsync_copy.bash @@ -0,0 +1,408 @@ +#!/bin/bash +# +# SCRIPT: generic_FS_rsync_copy.bash +# AUTHOR: Randy Michael +# DATE: 1/14/2008 +# REV: 1.2.1 +# +# PURPOSE: This script is used to replicate +# +# +# EXIT CODES: +# 0 ==> Normal execution. +# 2 ==> Remote host is not pingable. +# 3 ==> Copy verification failed. One or +# more local files are not the same +# size as the remote files. +# 4 ==> No machines are defined to copy +# data to. +# 5 ==> Exited on a trapped signal. +# +# set -x # Uncomment to debug this script +# +# set -n # Uncomment to check the script.s syntax +# # without any execution. Do not forget to +# # recomment this line! +# +######################################## +# REVISION LIST: +######################################## +# +# IF YOU MODIFY THIS SCRIPT, DOCUMENT THE CHANGE(S)!!! +# +######################################## +# +# Revised by: +# Revision Date: +# Revision: +# +############################################## +# DEFINE FILES AND GLOBAL VARIABLES HERE +############################################## + +# Define the target machines to copy data to. +# To specify more than one host enclose the +# hostnames in double quotes and put at least +# one space between each hostname +# +# EXAMPLE: MACHINE_LIST="fred yogi booboo" + +MACHINE_LIST="fred" + +# The FS_PATTERN variable defines the regular expression +# matching the filesystems we want to replicate with rsync. + +FS_PATTERN="/data0[1-5]" + +# Add /usr/local/bin to the PATH + +export PATH=$PATH:/usr/local/bin + +# Define the directory containing all of the +# output files this shell script will produce + +WORK_DIR=/usr/local/bin + +# Define the rsync script log file + +LOGFILE=${WORK_DIR}/generic_FS_rsync_copy.log + +# Capture the shell script file name + +THIS_SCRIPT=$(basename $0) + +# Initialize the background process ID list +# variable to NULL + +BG_PID_LIST= + +# Define the file containing a list of files to verify +# the sizes match locally and remotely + +LS_FILES=ls_output.dat +>$LS_FILES + +# Initialize the TOTAL variable to 0 + +TOTAL=0 + +# Query the system for the hostname + +THIS_HOST=$(hostname) + +# Query the system for the UNIX flavor + +THIS_OS=$(uname) + +############################################## +# DEFINE FUNCTIONS HERE +############################################## + +verify_copy () +{ +# set -x + +MYLOGFILE=${WORK_DIR}/verify_rsync_copy_day.log +>$MYLOGFILE +ERROR=0 + +# Enclose this loop so we can redirect output to the log file +# with one assignment at the bottom of the function + +{ + +# Put a header for the verification log file + +echo -e "\nRsync copy verification between $THIS_HOST and machine(s) \ +$MACHINE_LIST\n\n" >> $MYLOGFILE + +# Loop through each machine in the $MACHINE_LIST + +for M in $MACHINE_LIST +do + # Loop through each filesystem that matches the regular + # regular expression point to by "$FS_PATTERN". It is + # important that this variable is enclosed within double + # quotes. Also note the column for the mount point is 6; + # in AIX this should be changed to 7. + + for MP in $(df | grep "$FS_PATTERN" | awk '{print $6}') + do + # For each filesystem mount point, $MP, execute a + # find command to find all files in the filesystem. + # and store them in the $LS_FILES file. We will use this + # file list to verify the file sizes on the remote + # machines match the file sizes on the local machine. + + find $MP -type f > $LS_FILES + + # Loop through the file list + + for FL in $(cat $LS_FILES) + do + # Find the local file size + LOC_FS=$(ls -l $FL | awk '{print $5}' 2>&1) + # Find the remote file size using a remote shell command + REM_FS=$(rsh $M ls -l $FL | awk '{print $5}' 2>&1) + echo "Checking File: $FL" + echo -e "Local $THIS_HOST size:\t$LOC_FS" + echo -e "Remote $M size:\t$REM_FS" + + # Ensure the file sizes match + if [ "$LOC_FS" -ne "$REM_FS" ] + then + echo "ERROR: File size mismatch between $THIS_HOST and $M" + echo "File is: $FL" + ERROR=1 + fi + done + done +done + +if (( ERROR != 0 )) +then + # Record the failure in the log file + echo -e "\n\nRSYNC ERROR: $THIS_HOST Rsync copy failed...file size mismatch...\n\n" | tee -a $MYLOGFILE + echo -e "\nERROR: Rsync copy Failed!" + echo -e "\n\nCheck log file: $MYLOGFILE\n\n" + echo -e "\n...Exiting...\n" + return 3 +else + echo -e "\nSUCCESS: Rsync copy completed successfully..." + echo -e "\nAll file sizes match...\n" +fi +} | 2>&1 tee -a $MYLOGFILE + +} + +############################################## + +elapsed_time () +{ +SEC=$1 +(( SEC < 60 )) && echo -e "[Elapsed time: $SEC seconds]\c" + +(( SEC >= 60 && SEC < 3600 )) && echo -e "[Elapsed time: $(( SEC / 60 )) min $(( SEC % 60 )) sec]\c" + +(( SEC > 3600 )) && echo -e "[Elapsed time: $(( SEC / 3600 )) hr $(( (SEC % 3600) / 60 )) min $(( (SEC % 3600) % 60 )) sec]\c" +} + +############################################## +# BEGINNING OF MAIN +############################################## + +# Enclose the entire main part of the script in +# curly braces so we can redirect all output of +# the shell script with a single redirection +# at the bottom of the script to the $LOGFILE + +{ + +# Save a copy of the last log file + +cp -f $LOGFILE ${LOGFILE}.yesterday + +echo -e "\n[[ $THIS_SCRIPT started execution $(date) ]]\n" + +# Ensure that target machines are defined + +if [ -z "$MACHINE_LIST" ] +then + echo -e "\nERROR: No machines are defined to copy data to..." + echo "...Unable to continue...Exiting..." + exit 4 +fi + +# Ensure the remote machines are reachable through +# the network by sending 1 ping. + +echo -e "Verifying the target machines are pingable...\n" + +for M in $MACHINE_LIST +do + echo "Pinging $M..." + ping -c1 $M >/dev/null 2>&1 + if (( $? != 0 )) + then + echo -e "\nERROR: $M host is not pingable...cannot continue..." + echo -e "...EXITING...\n" + exit 2 + else + echo "Pinging $M succeeded..." + fi +done + +# Start all of the rsync copy sessions at once by looping +# through each of the mount points and issuing an rsync +# command in the background, and incrementing a counter. + +echo -e "\nStarting a bunch of rsync sessions...\n" + +for M in $MACHINE_LIST +do + # Loop through each filesystem that matches the regular + # expression point to by "$FS_PATTERN". It is + # important that this variable is enclosed within double + # quotes. Also note the column for the mount point is 6; + # in AIX this should be changed to 7. + + for MP in $(df | grep "$FS_PATTERN" | awk '{print $6}') + do + # Start the rsync session in the background + + time rsync -avz ${MP}/ ${M}:${MP} 2>&1 & + + # Keep a running total of the number of rsync + # sessions started + + (( TOTAL = TOTAL + 1 )) + done +done + +# Sleep a few seconds before monitoring processes + +sleep 10 + +# Find the number of rsync sessions that are still running + +REM_SESSIONS=$(ps x | grep "rsync -avz" | grep "$FS_PATTERN" \ + | grep -v grep | awk '{print $1}' | wc -l) + +# Give some feedback to the end user. + +if (( REM_SESSIONS > 0 )) +then + echo -e "\n$REM_SESSIONS of $TOTAL rsync copy sessions require \ +further updating..." + echo -e "\nProcessing rsync copies from $THIS_HOST to both \ +$MACHINE_LIST machines" + echo -e "\nPlease be patient, this process may take a very long \ +time...\n" + echo -e "Rsync is running [Start time: $(date)]\c" +else + echo -e "\nAll files appear to be in sync...verifying file sizes...\ +Please wait...\n" +fi + +# While the rsync processes are executing this loop +# will place a . (dot) every 60 seconds as feedback +# to the end user that the background rsync copy +# processes are still executing. When the remaining +# rsync sessions are less than the total number of +# sessions (normally 36) this loop will give feedback +# as to the number of remaining rsync session, as well +# as the elapsed time of the processing, every 5 minutes.. + +SECONDS=10 +MIN_COUNTER=0 + +# Loop until all of the local rsync sessions have completed + +until (( REM_SESSIONS == 0 )) +do + # sleep 60 seconds between loop iterations + + sleep 60 + + # Display a dot on the screen every 60 seconds + + echo -e ".\c" + + # Find the number of remaining rsync sessions + + REM_SESSIONS=$(ps x | grep "rsync -avz" | grep "$FS_PATTERN" \ + | grep -v grep | '{print $1}' | wc -l) + +# Have any of the sessions completed? If so start giving +# the user updates every 5 minutes, specifying the number +# remaining rsync sessions and the elapsed time. + +if (( REM_SESSIONS < TOTAL )) +then + # Count every 5 minutes + (( MIN_COUNTER = MIN_COUNTER + 1 )) + + # 5 minutes timed out yet? + if (( MIN_COUNTER >= 5 )) + then + # Reset the minute counter + MIN_COUNTER=0 + + # Update the user with a new progress report + + echo -e "\n$REM_SESSIONS of $TOTAL rsync sessions \ +remaining $(elapsed_time $SECONDS)\c" + + # Have three-fourths of the rsync sessions completed? + + if (( REM_SESSIONS <= $(( TOTAL / 4 )) )) + then + # Display the list of remaining rsync sessions + # that continue to run. + + echo -e "\nRemaining rsync sessions include:\n" + ps x | grep "rsync -avz" | grep "$FS_PATTERN" \ + | grep -v grep + echo + fi + fi + fi +done + +echo -e "\n...Local rsync processing completed on $THIS_HOST...$(date)" +echo -e "\n...Checking remote target machines: $MACHINE_LIST..." + +# Just because the local rsync processes have completed does +# not mean that the remote rsync processes have completed +# execution. This loop verifies that all of the remote +# rsync processes completed execution. The end user will +# receive feedback if any of the remote processes are running. + +for M in $MACHINE_LIST +do + # Loop through each matching filesystem + + for MP in $(df | grep "$FS_PATTERN" | awk '{print $7}') + do + # Find the remote process Ids. + RPID=$(rsh $M ps x | grep rsync | grep $MP | grep -v grep \ + | awk '{print $1}') + + # Loop while remote rsync sessions continue to run. + # NOTE: I have never found remote rsync sessions running + # after the local sessions have completed. This is just + # an extra sanity check + + until [ -z "$RPID" ] + do + echo "rsync is processing ${MP} on ${M}...sleeping one minute..." + sleep 60 + RPID=$(rsh $M ps x | grep rsync | grep $MP | grep -v grep \ + | awk '{print $1}') + done + done +done + +echo -e "\n...Remote rsync processing completed $(date)\n" + +# Verify the copy process + +verify_copy + +# Check the verify_copy return code + +if (( $? != 0 )) +then + exit 3 +fi + +echo -e "\nRsync copy completed $(date)" + +echo -e "\n[[ $THIS_SCRIPT completed execution $(date) ]]\n" +echo -e "\n[[ Elapsed Time: $(elapsed_time $SECONDS) ]]\n" + +} | 2>&1 tee -a $LOGFILE + +############################################### +# END OF SCRIPT +############################################### diff --git a/chapter7/generic_rsync.bash b/chapter7/generic_rsync.bash new file mode 100755 index 0000000..9dbe37b --- /dev/null +++ b/chapter7/generic_rsync.bash @@ -0,0 +1,33 @@ +#!/bin/bash +# +# SCRIPT: generic_rsync.bash +# AUTHOR: Randy Michael +# DATE: 11/18/2007 +# REV: 1.0 +# PURPOSE: This is a generic shell script to copy files +# using rsync. +# +# set -n # Uncomment to check script syntax without execution +# set -x # Uncomment to debug this script +# +# REV LIST: +# +# +############################################## +# DEFINE FILES AND VARIABLES HERE +############################################## + +# Define the source and destination files/directories + +SOURCE_FL="/scripts/" +DESTIN_FL="booboo:/scripts" + +############################################## +# BEGINNING OF MAIN +############################################## + +# Start the rsync copy + +rsync -avz "$SOURCE_FL" "$DESTIN_FL" + +# End of generic_rsync.bash diff --git a/chapter7/rsync_daily_copy.ksh b/chapter7/rsync_daily_copy.ksh new file mode 100755 index 0000000..53cabaa --- /dev/null +++ b/chapter7/rsync_daily_copy.ksh @@ -0,0 +1,545 @@ +#!/bin/ksh +# +# SCRIPT: rsync_daily_copy.ksh +# AUTHOR: Randy Michael +# DATE: 11/10/2007 +# REV: 3.2.Prod +# +# PURPOSE: This script is used to replicate Oracle .dbf +# files between the "master" DB server and the two +# OLTP Oracle servers. The copy method used is +# rsync. For this method to work, the Oracle DBA must +# first put the database tables that reside in the +# copy filesystems into READ-ONLY mode. Before starting +# rsync copy sessions, this script searches for a file, +# defined by the $READYTORUN_FILE variable, that is +# placed on the system by the Oracle DBA team; when +# the file is found this script will execute +# all 36 rsync sessions, 18 to each OLTP server, +# at the same time, then waits for all sessions to +# complete, both locally and on the remote servers. +# After the rsync copy sessions complete the copy is +# verified by matching file sizes between the master +# copy file and the target copy files. When verified +# successful this script writes a file to the system, +# defined by the $COMPLETE_FILE variable, to signal +# the Oracle DBAs to put the DB back into READ-WRITE +# mode, copy the metadata over, build the tables and +# attach the DB on the OLTP side servers. Should a +# failure occur a file, defined by the $RSYNCFAILED_FILE +# variable, is written to the system to signal to the +# Oracle DBA team an rsync copy process failure. +# +# +# EXIT CODES: +# 0 ==> Normal execution. +# 1 ==> The value assigned to DAY is not +# an integer 1 or 2. +# 2 ==> Remote host is not pingable. +# 3 ==> Copy verification failed. One or +# more local files are not the same +# size as the remote files. +# 4 ==> No machines are defined to copy +# data to. +# 5 ==> Exited on a trapped signal. +# +# set -x # Uncomment to debug this script +# +# set -n # Uncomment to check the script's syntax +# # without any execution. Do not forget to +# # recomment this line! +# +######################################## +# REVISION LIST: +######################################## +# +# IF YOU MODIFY THIS SCRIPT DOCUMENT THE CHANGE(S)!!! +# +######################################## +# +# Revised by: Randy Michael +# Revision Date: 7/23/2007 +# Revision: Changed the script to process +# all 36 mount points at a time. +# +######################################## +# +# Revised by: +# Revision Date: +# Revision: +# +############################################## +# DEFINE FILES AND GLOBAL VARIABLES HERE +############################################## + +typeset -i DAY +EMAIL_FROM=data_support@gamma +export PATH=$PATH:/usr/local/bin +WORK_DIR=/usr/local/bin +LOGFILE=${WORK_DIR}/rsync_daily_copy.log +SEARCH_DIR=/orabin/apps/oracle/dbadm/general/bin +READYTORUN_FILE=${SEARCH_DIR}/readytocopy.txt +COMPLETE_FILE=${SEARCH_DIR}/copycomplete.txt +RSYNCFAILED_FILE=${SEARCH_DIR}/copyfailed.txt +MAILMESSAGEFILE=${WORK_DIR}/email_message.out +THIS_SCRIPT=$(basename $0) +BG_PID_LIST= +TOTAL=0 +THIS_HOST=$(hostname) +[[ $THIS_HOST = gamma ]] && MACHINE_LIST="alpha-rsync bravo-rsync" +[[ $THIS_HOST = gamma-dg ]] && MACHINE_LIST="alpha-rsync bravo-rsync" + +# Setup the correct echo command usage. Many Linux +# distributions will execute in BASH even if the +# script specifies Korn shell. BASH shell requires +# we use echo -e when we use \n, \c, etc. + +case $SHELL in +*/bin/bash) alias echo="echo -e" + ;; +esac + +############################################## +# DEFINE FUNCTIONS HERE +############################################## + +usage () +{ +echo "\nUSAGE: $THIS_SCRIPT Day" +echo "\nWhere Day is 1 or 2\n" +} + +############################################## + +cleanup_exit () +{ +# If this script is executing, then something failed! + +[ $1 ] && EXIT_CODE=$1 + +echo "\n$THIS_SCRIPT is exiting on non-zero exit code: $EXIT_CODE" +echo "\nPerforming cleanup..." +echo "Removing $READYTORUN_FILE" +rm -f $READYTORUN_FILE >/dev/null 2>&1 +echo "Removing $COMPLETE_FILE" +rm -f $COMPLETE_FILE >/dev/null 2>&1 +echo "\nCreating $RSYNCFAILED_FILE" +echo "\nRsync failed on $THIS_HOST with exit code $EXIT_CODE $(date)\n"\ + | tee -a $RSYNCFAILED_FILE +echo "\nCleanup Complete...Exiting..." +return $EXIT_CODE +exit $EXIT_CODE +} + +############################################## + +trap_exit () +{ +echo "\nERROR: EXITING ON A TRAPPED SIGNAL!\n" +echo "\nRSYNC ERROR: $THIS_HOST -- Rsync process exited abnormally on a \ +trapped signal $(date)!" > $MAILMESSAGEFILE + +mailx -r "$EMAIL_FROM" -s "SYNC ERROR: $THIS_HOST -- Rsync process exited \ +abnormally on a trapped signal!" data_support < $MAILMESSAGEFILE + +sleep 2 # Allow the email to go out +cleanup_exit 5 +return 5 +exit 5 +} + +############################################## + +verify_copy () +{ +#set -x + +MYLOGFILE=${WORK_DIR}/verify_rsync_copy_day${DAY}.log +>$MYLOGFILE +ERROR=0 + +# Enclose this loop so we can redirect output to the log file +# with one assignment at the bottom of the function +{ +# Put a header for the verification log file + +echo "\nRsync copy verification between $THIS_HOST and machines $MACHINE_LIST\n\n" >$MYLOGFILE + + +for M in $MACHINE_LIST +do + for LOC_MP in $(df | grep oradata_dm_[0-2][0-9] | awk '{print $7}') + do + LS_FILES=$(find $LOC_MP -type f) + for FL in $LS_FILES + do + LOC_FS=$(ls -l $FL | awk '{print $5}' 2>&1) + + # This sed statement changes the "m" to $DAY + REM_FL=$(echo $FL | sed s/oradata_dm_/oradata_d${DAY}_/g) + + REM_FS=$(rsh $M ls -l $REM_FL | awk '{print $5}' 2>&1) + echo "Checking File: $FL" + echo "Local $THIS_HOST size:\t$LOC_FS" + echo "Checking Remote File: $REM_FL" + echo "$M size:\t$REM_FS" + if [ "$LOC_FS" -ne "$REM_FS" ] + then + echo "ERROR: File size mismatch between $THIS_HOST and $M" + echo "File is: $FL" + ERROR=1 + fi + done + done +done + +if (( ERROR != 0 )) +then + # Record the failure in the log file + + echo "\n\nRSYNC ERROR: $THIS_HOST Rsync copy failed...file size \ +mismatch...\n\n" | tee -a $MYLOGFILE + + # Send email notification with file size log + + mailx -r "$EMAIL_FROM" -s "RSYNC ERROR: $THIS_HOST Rsync copy failed\ +...file size mismatch -- log attached" data_support < $MYLOGFILE + + echo "\nERROR: Rsync copy Failed!" + echo "\n\nCheck log file: $MYLOGFILE\n\n" + echo "\n...Exiting...\n" + cleanup_exit 3 + return 3 +else + echo "\nSUCCESS: Rsync copy completed successfully..." + echo "\nAll file sizes match...\n" +fi +} | tee -a $MYLOGFILE + +} + +######################################## + +ready_to_run () +{ +# set -x +# This function looks for a file on the system +# defined by the $READYTORUN_FILE variable. The +# presents presence of this file indicates we are ready +# to run this script. This file will contain a +# number 1 or 2 identifying the day we are +# working with. + +if [ -r ${READYTORUN_FILE} ] +then + cat ${READYTORUN_FILE} +else + echo "NOT_READY" +fi +} + +############################################## + +elapsed_time () +{ +SEC=$1 +(( SEC < 60 )) && echo "[Elapsed time: $SEC seconds]\c" + +(( SEC >= 60 && SEC < 3600 )) && echo "[Elapsed time: $(( SEC / 60 )) \ +min $(( SEC % 60 )) sec]\c" + +(( SEC > 3600 )) && echo "[Elapsed time: $(( SEC / 3600 )) hr $(( (SEC % 3600) / 60 )) \ +min $(( (SEC % 3600) % 60 )) sec]\c" +} + +############################################## +# BEGINNING OF MAIN +############################################## + +# Set a trap + +trap 'trap_exit' 1 2 3 5 6 11 14 15 17 + +# Save the old log file + +cp -f $LOGFILE ${LOGFILE}.yesterday \ + >$LOGFILE + +# Enclose the entire main part of the script in +# curly braces so we can redirect all output of +# the shell script with a single redirection +# at the bottom of the script to the $LOGFILE + +{ +echo "\n[[ $THIS_SCRIPT started execution $(date) ]]\n" + +# Ensure that target machines are defined + +if [ -z "$MACHINE_LIST" ] +then + echo "\nERROR: No machines are defined to copy data to..." + echo "\nRSYNC ERROR: $THIS_HOST has no machines are defined \ +to copy data to..." > $MAILMESSAGEFILE + mailx -r "$EMAIL_FROM" -s "RSYNC ERROR: $THIS_HOST has no machines \ +defined to copy data to" data_support < $MAILMESSAGEFILE + echo "...Unable to continue...Exiting..." + + cleanup_exit 4 + exit 4 +fi + +# Checking for currently running versions of this script + +echo "Checking for Currently Runnning Versions of this Script" + +MYPID=$$ # Capture this scripts PID + +MYOTHERPROCESSES=$(ps -ef | grep $THIS_SCRIPT | grep -v $MYPID \ + | grep -v grep | awk '{print $2}') + +if [[ "$MYOTHERPROCESSES" != "" ]] +then + echo "\WARNING: Another version of this script is running...Killing it!" + echo "Killing the following processe(es)...$MYOTHERPROCESSES" + kill -9 $MYOTHERPROCESSES + echo "\nNOTICE: Sleeping for 1 minute to allow both local +and remote rsync sessions to terminate, if they exist...\n" + sleep 60 # Allow any rsync sessions to stop both locally and remotely +else + echo "No other versions running...proceeding" +fi + +# Remove the file that indicates the rsync copy +# is mounted and ready to use, if it exists. + +rm -f $COMPLETE_FILE >/dev/null 2>&1 + +# Remove the file that indicates the rsync copy +# failed and exited on a non-zero exit code, if +# it exists. + +rm -f $RSYNCFAILED_FILE >/dev/null 2>&1 + + +# Search for the file indicating we are ready to proceed + +# Send notification + +echo "\nDaily Rsync Copy Process Began on Host $THIS_HOST $(date)" \ + > $MAILMESSAGEFILE +echo "\nStarted looking for $READYTORUN_FILE" >> $MAILMESSAGEFILE + +mailx -r "$EMAIL_FROM" -s "Rsync Copy Process Began Looking for Startup \ +File on $THIS_HOST" data_support < $MAILMESSAGEFILE + +echo "\nSearching for the file: ${READYTORUN_FILE}" +RUN_STATUS=$(ready_to_run) +until [[ $RUN_STATUS != "NOT_READY" ]] +do + date + echo "$READYTORUN_FILE is not present...Sleeping 5 minutes..." + sleep 300 + RUN_STATUS=$(ready_to_run) +done +date +echo "Found file: $READYTORUN_FILE -- Ready to proceed..." +DAY=$RUN_STATUS + + +# Test the value assigned to the DAY variable + +echo "Testing variable assignment for the DAY variable" + +if (( DAY != 1 && DAY != 2 )) +then + echo "\nRSYNC ERROR: $THIS_HOST -- The value assigned to \ +the DAY variable" > $MAILMESSAGEFILE + echo "==> $DAY - is not an integer 1 or 2...Exiting..." + >> $MAILMESSAGEFILE + mailx -r "$EMAIL_FROM" -s "ERROR: $THIS_HOST -- Value assigned \ +to the DAY variable $DAY is invalid" data_support < $MAILMESSAGEFILE + + usage + cleanup_exit + exit 1 +fi + +# Ensure the remote machines are reachable through +# the network by sending 1 ping. + +echo "Verifying the target machines are pingable...\n" +for M in $MACHINE_LIST +do + echo "Pinging $M..." + ping -c1 $M >/dev/null 2>&1 + if (( $? != 0 )) + then + echo "RSYNC ERROR: $M is not pingable from $THIS_HOST" \ + > $MAILMESSAGEFILE + echo "The rsync copy process cannot continue...Exiting" \ + >> $MAILMESSAGEFILE + mailx -r "$EMAIL_FROM" -s "RSYNC ERROR: $M is not pingable +from $THIS_HOST" data_support < $MAILMESSAGEFILE + + echo "\nERROR: $M host is not pingable...cannot continue..." + echo "...EXITING...\n" + sleep 2 + cleanup_exit 2 + exit 2 + else + echo "Pinging $M succeeded..." + fi +done + +# Notify start of rsync processing by email + +echo "Rsync Copy Process for Day $DAY Starting Execution on \ +$THIS_HOST $(date)" > $MAILMESSAGEFILE + +mailx -r "$EMAIL_FROM" -s "Rsync copy process for day $DAY starting \ +on $THIS_HOST" data_support < $MAILMESSAGEFILE + +# Start all of the rsync copy sessions at once by looping +# through each of the mount points and issuing an rsync +# command in the background, and incrementing a counter. + +echo "\nStarting a bunch of rsync sessions...\n" + +# This script copies from the DM filesystems to the +# Day 1 or Day 2 filesystems on the OLTP servers. + +for M in $MACHINE_LIST +do + for LOC_MP in $(df | grep oradata_dm_[0-2][0-9] | awk '{print $7}') + do + # This sed statement changes the "m" to $DAY + REM_MP=$(echo $LOC_MP | sed s/m/$DAY/g) + time rsync -avz ${LOC_MP}/ ${M}:${REM_MP} 2>&1 & + (( TOTAL = TOTAL + 1 )) + done +done + +# Sleep a few seconds before monitoring processes + +sleep 10 + +# Give some feedback to the end user. + +REM_SESSIONS=$(ps -ef | grep "rsync -avz" | grep oradata_dm_[0-2][0-9] \ + | grep -v grep | awk '{print $2}' | wc -l) + +if (( REM_SESSIONS > 0 )) +then + echo "\n$REM_SESSIONS of $TOTAL rsync copy sessions require further updating..." + echo "\nProcessing rsync copies from $THIS_HOST to both $MACHINE_LIST machines" + echo "\nPlease be patient, this process may take longer than two hours...\n" + echo "Rsync is running [Start time: $(date)]\c" +else + echo "\nAll files appear to be in sync...verifying file sizes...Please wait...\n" +fi + +# While the rsync processes are executing this loop +# will place a . (dot) every 60 seconds as feedback +# to the end user that the background rsync copy +# processes are still executing. When the remaining +# rsync sessions are less than the total number of +# sessions (normally 36) this loop will give feedback +# as to the number of remaining rsync session, as well +# as the elapsed time of the procesing, every 5 minutes. + +SECONDS=10 +MIN_COUNTER=0 + +until (( REM_SESSIONS == 0 )) +do + sleep 60 + echo ".\c" + REM_SESSIONS=$(ps -ef | grep "rsync -avz" | grep oradata_dm_[0-2][0-9] \ + | grep -v grep | awk '{print $2}' | wc -l) + if (( REM_SESSIONS < TOTAL )) + then + (( MIN_COUNTER = MIN_COUNTER + 1 )) + if (( MIN_COUNTER >= 5 )) + then + MIN_COUNTER=0 + echo "\n$REM_SESSIONS of $TOTAL rsync sessions \ +remaining $(elapsed_time $SECONDS)\c" + if (( REM_SESSIONS <= $(( TOTAL / 4 )) )) + then + echo "\nRemaining rsync sessions include:\n" + ps -ef | grep "rsync -avz" | grep oradata_dm_[0-2][0-9] \ + | grep -v grep + echo + fi + fi + fi +done + +echo "\n...Local rsync processing completed on $THIS_HOST...$(date)" +echo "\n...Checking remote target machines: $MACHINE_LIST..." + +# Just because the local rsync processes have completed does +# not mean that the remote rsync processes have completed +# execution. This loop verifies that all of the remote +# rsync processes completed execution. The end user will +# receive feedback if any of the remote processes are running. + +for M in $MACHINE_LIST +do + for LOC_MP in $(df | grep oradata_dm_[0-2][0-9] | awk '{print $7}') + do + # This sed statement changes the "m" to $DAY + REM_MP=$(echo $LOC_MP | sed s/m/$DAY/g) + + RPID=$(rsh $M ps -ef | grep rsync | grep $REM_MP \ + | grep -v grep | awk '{print $2}') + + until [ -z "$RPID" ] + do + echo "rsync is processing ${REM_MP} on ${M}...sleeping one minute..." + sleep 60 + RPID=$(rsh $M ps -ef | grep rsync | grep $REM_MP \ + | grep -v grep | awk '{print $2}') + done + done +done + +echo "\n...Remote rsync processing completed $(date)\n" + +# Verify the copy process + +verify_copy +if (( $? != 0 )) +then + exit 3 +fi + +# Write to the $COMPLETE_FILE to signal the Oracle DBAs +# that the copy has completed + +echo "Rsync copy from $THIS_HOST to machines $MACHINE_LIST \ +completed successfully $(date)" | tee -a $COMPLETE_FILE + +# Remove the ready to run file + +rm -f $READYTORUN_FILE >/dev/null 2>&1 + +# Notify completion by email + +echo "Rsync Copy for Day $DAY Completed Successful Execution \ +on $THIS_HOST $(date)\n" > $MAILMESSAGEFILE +elapsed_time $SECONDS >> $MAILMESSAGEFILE +mailx -r "$EMAIL_FROM" -s "Rsync copy for day $DAY completed \ +successfully on $THIS_HOST" data_support < $MAILMESSAGEFILE + +echo "\nRsync copy completed $(date)" + +echo "\n[[ $THIS_SCRIPT completed execution $(date) ]]\n" + +exit 0 + +} | 2>&1 tee -a $LOGFILE + +############################################### +# END OF SCRIPT +############################################### diff --git a/chapter8/boot-net-install.exp b/chapter8/boot-net-install.exp new file mode 100755 index 0000000..466c2a2 --- /dev/null +++ b/chapter8/boot-net-install.exp @@ -0,0 +1,35 @@ +#!/usr/local/bin/expect -f + +set force_conservative 0 ;# set to 1 to force conservative mode even if + ;# script wasn.t run conservatively originally +if {$force_conservative} { + set send_slow {1 .1} + proc send {ignore arg} { + sleep .1 + exp_send -s -- $arg + } +} + +set chassis [lindex $argv 0] +set blade [lindex $argv 1] +set timeout -1 +spawn -noecho telnet $chassis +log_user 0 +match_max 100000 +expect "username: " +send -- "prodmgr\r" +expect -exact "prodmgr\r +password: " +send -- "Abc1234\r" +expect ">" +send -- "console -f $blade\r" +send -- "\r" +expect "ok" +send -- "boot net - install\r" +log_user 1 +expect "" +send -- "logout\r" +send_user "\r\n" +exit +expect eof + diff --git a/chapter8/etc-hosts-copy.exp b/chapter8/etc-hosts-copy.exp new file mode 100755 index 0000000..3d2b9db --- /dev/null +++ b/chapter8/etc-hosts-copy.exp @@ -0,0 +1,101 @@ +#!/usr/local/bin/expect -f +# +# SCRIPT: etc-hosts-copy.exp + +# Set the timeout to 3 seconds +set timeout 3 + +# Assign the first command line argument to the +# "host" variable +set rem_host [lindex $argv 0] + +# Spawn an ssh session to $host as the prodmgr user +spawn /usr/bin/ssh -l prodmgr ${rem_host} + +# When we ssh to a remote server we can have two +# possible responses, 1) "*re you sure you want to continue +# connecting*", which we must reply to with "yes", and +# 2) Password: prompt. This next expect statement +# will perform the correct action based on the +# response. + +expect { + "*re you sure you want to continue connecting*" + { + send "yes\n" + expect { + "*assword*" + { + send "Abc1234\n" + } + } + } + "*assword*" + { + send "Abc1234\n" + } + } + +# Sleep for 1 second before proceeding + +sleep 1 + +# Set the command-line prompt for the prodmgr user +# to the hash mark, # + +send "PS1\=\"\# \"\n " + +# Now we know what to expect as a command-line prompt + +expect "^#*" + +# Copy the master /etc/hosts file from yogi +# to this machine. + +# First su to root + +send "su - \n" +expect "*assword:*" +send "ABCD1234\n" +sleep 1 + +# Set the command-line prompt for the root user +# to the hash mark, # + +send "PS1\=\"\# \"\n " + +# Now we know what the command-line prompt is for +# the root user +expect "^#*" + +# Save the current version of the /etc/hosts file +send "cp /etc/hosts /etc/hosts$$\n" +expect { + "*re you sure you want to continue connecting*" + { + send "yes\n" + expect { + "*assword*" + { + send "Abc1234\n" + } + } + } + "*assword*" + { + send "Abc1234\n" + } + "^#*" + { + send "\n" + } + } + +# Copy the master /etc/hosts file from yogi to +# this server. +send "rcp yogi:/etc/hosts /etc/hosts \n" +expect "^#*" + +# Logout from $host +send "exit\n" +expect eof diff --git a/chapter8/findslot b/chapter8/findslot new file mode 100755 index 0000000..50f6cfe --- /dev/null +++ b/chapter8/findslot @@ -0,0 +1,13 @@ +#!/usr/bin/bash +# +# SCRIPT: findslot +# PURPOSE: This script is used to locate a blade server +# within the specified SUN blade chassis. +# + +CHAS=$1 +HOST=$2 + +SLOT=`/usr/local/bin/showplatform.exp $CHAS \ + | /usr/bin/grep $HOST | cut -f 1 -d " "` +echo $SLOT diff --git a/chapter8/ftp-get-file-cmd-line.exp b/chapter8/ftp-get-file-cmd-line.exp new file mode 100755 index 0000000..055b241 --- /dev/null +++ b/chapter8/ftp-get-file-cmd-line.exp @@ -0,0 +1,33 @@ +#!/usr/local/bin/expect -f +# +# SCRIPT: ftp-get-file-cmd-line.exp +# + +set force_conservative 0 ;# set to 1 to force conservative mode even if + ;# script wasn't run conservatively originally +if {$force_conservative} { + set send_slow {1 .1} + proc send {ignore arg} { + sleep .1 + exp_send -s -- $arg + } +} + +set rem_host [lindex $argv 0] +set rem_dir [lindex $argv 1] +set rem_file [lindex $argv 2] + +set timeout -1 +spawn ftp $rem_host +match_max 100000 +expect "Name *: " +send -- "prodmgr\r" +expect "Password:" +send -- "Abc1234\r" +expect "ftp>" +send -- "cd $rem_dir \r" +expect "ftp>" +send -- "get $rem_file \r" +expect "ftp>" +send -- "bye\r" +expect eof diff --git a/chapter8/ftp-get-file.exp b/chapter8/ftp-get-file.exp new file mode 100755 index 0000000..efc8891 --- /dev/null +++ b/chapter8/ftp-get-file.exp @@ -0,0 +1,30 @@ +#!/usr/bin/expect -f +# +# SCRIPT: ftp-get-file.exp +# + +set force_conservative 0 ;# set to 1 to force conservative mode even if + ;# script wasn't run conservatively originally +if {$force_conservative} { + set send_slow {1 .1} + proc send {ignore arg} { + sleep .1 + exp_send -s -- $arg + } +} + + +set timeout -1 +spawn ftp yogi +match_max 100000 +expect "Name *: " +send -- "prodmgr\r" +expect "Password:" +send -- "Abc1234\r" +expect "ftp>" +send -- "cd /scripts\r" +expect "ftp>" +send -- "get random_file.out\r" +expect "ftp>" +send -- "bye\r" +expect eof diff --git a/chapter8/proc_increment_by_1 b/chapter8/proc_increment_by_1 new file mode 100755 index 0000000..51f9b78 --- /dev/null +++ b/chapter8/proc_increment_by_1 @@ -0,0 +1,4 @@ +proc increment_by_1 { MY_COUNT } { + set MY_COUNT [expr $MY_COUNT + 1] + return "$MY_COUNT" +} diff --git a/chapter8/showplatform.exp b/chapter8/showplatform.exp new file mode 100755 index 0000000..c170a26 --- /dev/null +++ b/chapter8/showplatform.exp @@ -0,0 +1,62 @@ +#!/usr/local/bin/expect -f +# +# This Expect script was generated by autoexpect on Mon Apr 2 09:44:34 2007 +# Expect and autoexpect were both written by Don Libes, NIST. +# +# Note that autoexpect does not guarantee a working script. It +# necessarily has to guess about certain things. Two reasons a script +# might fail are: +# +# 1) timing - A surprising number of programs (rn, ksh, zsh, telnet, +# etc.) and devices discard or ignore keystrokes that arrive "too +# quickly" after prompts. If you find your new script hanging up at +# one spot, try adding a short sleep just before the previous send. +# Setting "force_conservative" to 1 (see below) makes Expect do this +# automatically - pausing briefly before sending each character. This +# pacifies every program I know of. The -c flag makes the script do +# this in the first place. The -C flag allows you to define a +# character to toggle this mode off and on. + +set force_conservative 0 ;# set to 1 to force conservative mode even if + ;# script wasn't run conservatively originally +if {$force_conservative} { + set send_slow {1 .1} + proc send {ignore arg} { + sleep .1 + exp_send -s -- $arg + } +} + +# +# 2) differing output - Some programs produce different output each time +# they run. The "date" command is an obvious example. Another is +# ftp, if it produces throughput statistics at the end of a file +# transfer. If this causes a problem, delete these patterns or replace +# them with wildcards. An alternative is to use the -p flag (for +# "prompt") which makes Expect only look for the last line of output +# (i.e., the prompt). The -P flag allows you to define a character to +# toggle this mode off and on. +# +# Read the man page for more info. +# +# -Don + +set chassis [lindex $argv 0] +set timeout -1 +spawn $env(SHELL) +send -- "telnet ${chassis}\r" +expect -exact "telnet ${host}\r" +expect -exact "username: " +send -- "prodmgr\r" +expect -exact "prodmgr\r +password: " +send -- "Abc1234\r" +expect -exact "\r +${host}>" +send -- "showplatform -v\r" +expect -exact "showplatform -v\r" +send -- "logout\r" +expect -exact "logout\r" +exit +expect eof + diff --git a/chapter9/findlarge.ksh b/chapter9/findlarge.ksh new file mode 100755 index 0000000..6de5d33 --- /dev/null +++ b/chapter9/findlarge.ksh @@ -0,0 +1,135 @@ +#!/usr/bin/ksh +# +# SCRIPT:findlarge.ksh +# +# AUTHOR: Randy Michael +# +# DATE: 11/30/2007 +# +# REV: 1.0.A +# +# PURPOSE: This script is used to search for files which +# are larger than $1 Meg. Bytes. The search starts at +# the current directory that the user is in, `pwd`, and +# includes files in and below the user's current directory. +# The output is both displayed to the user and stored +# in a file for later review. +# +# REVISION LIST: +# +# +# +# set -x # Uncomment to debug this script + + +############################################ + +function usage +{ +echo "\n***************************************" +echo "\n\nUSAGE: findlarge.ksh [Number_Of_Meg_Bytes]" +echo "\nEXAMPLE: filelarge.ksh 5" +echo "\n\nWill Find Files Larger Than 5 Mb in, and Below, the +Current Directory..." +echo "\n\nEXITING...\n" +echo "\n***************************************" +exit +} + +############################################ + +function cleanup +{ +echo "\n********************************************************" +echo "\n\nEXITING ON A TRAPPED SIGNAL..." +echo "\n\n********************************************************\n" +exit +} + +############################################ + +# Set a trap to exit. REMEMBER - CANNOT TRAP ON kill -9 !!!! + +trap 'cleanup' 1 2 3 15 + +############################################ + +# Check for the correct number of arguments and a number +# Greater than zero + +if [ $# -ne 1 ] +then + usage +fi + +if [ $1 -lt 1 ] +then + usage +fi + +############################################ + +# Define and initialize files and variables here... + +THISHOST=`hostname` # Hostname of this machine + +DATESTAMP=`date +"%h%d:%Y:%T"` + +SEARCH_PATH=`pwd` # Top level directory to search + +MEG_BYTES=$1 # Number of Mb for file size trigger + +DATAFILE="/tmp/filesize_datafile.out" # Data storage file +>$DATAFILE # Initialize to a null file + +OUTFILE="/tmp/largefiles.out" # Output user file +>$OUTFILE # Initialize to a null file + +HOLDFILE="/tmp/temp_hold_file.out" # Temporary storage file +>$HOLDFILE # Initialize to a null file + +############################################ + +# Prepare the output user file + +echo "\nSearching for Files Larger Than ${MEG_BYTES}Mb starting in:" +echo "\n==> $SEARCH_PATH" +echo "\nPlease Standby for the Search Results..." +echo "\nLarge Files Search Results:" >> $OUTFILE +echo "\nHostname of Machine: $THISHOST" >> $OUTFILE +echo "\nTop Level Directory of Search:" >> $OUTFILE +echo "==> $SEARCH_PATH" >> $OUTFILE +echo "\nDate/Time of Search: `date`" >> $OUTFILE +echo "\n\nSearch Results Sorted by Time:" >> $OUTFILE + +############################################ + +# Search for files > $MEG_BYTES starting at the $SEARCH_PATH + +find $SEARCH_PATH -type f -size +${MEG_BYTES}000000c \ + -print > $HOLDFILE + +# How many files were found? + +if [ -s $HOLDFILE ] +then + NUMBER_OF_FILES=`cat $HOLDFILE | wc -l` + + echo "\nNumber of Files Found: ==> $NUMBER_OF_FILES\n" >> $OUTFILE + + # Append to the end of the Output File... + + ls -lt `cat $HOLDFILE` >> $OUTFILE + + # Display the Time Sorted Output File... + + more $OUTFILE + + echo "\nThese Search Results are Stored in ==> $OUTFILE" + echo "\nSearch Complete...EXITING...\n\n\n" +else + cat $OUTFILE + echo "\nNo Files were Found in the Search Path that" + echo "are Larger than ${MEG_BYTES}Mb\n" + echo "\nEXITING...\n\n" +fi