diff --git a/4271.pdf b/4271.pdf new file mode 100644 index 0000000..05e9ab5 Binary files /dev/null and b/4271.pdf differ diff --git a/4283.pdf b/4283.pdf new file mode 100644 index 0000000..8af0c61 Binary files /dev/null and b/4283.pdf differ diff --git a/9781430218418.jpg b/9781430218418.jpg new file mode 100644 index 0000000..f6f033f Binary files /dev/null and b/9781430218418.jpg differ diff --git a/Chapter_03/epoch_days b/Chapter_03/epoch_days new file mode 100644 index 0000000..0f2692b --- /dev/null +++ b/Chapter_03/epoch_days @@ -0,0 +1,27 @@ +#!/bin/bash + +# +# This script outputs the number of days since 1/1/1970 +# +seconds_gnu_epoch=`date +'%s'` + +# This is the equation that works for the Gregorian calendar for years from +# 1582 through 9999. If you need to calculate something else, you'll need +# a new equation. +epoch_days=$(((1969*365)+(1969/4)-(1969/100)+(1969/400)+(14*306001/10000)+1)) + +day=`date +'%d'` +month=`date +'%m' | sed 's/0*//'` +year=`date +'%Y'` + +if [ $month -gt 2 ] +then + month=$((month+1)) +else + month=$((month+13)) + year=$((year-1)) +fi + +today_days=$(((year*365)+(year/4)-(year/100)+(year/400)+(month*306001/10000)+day)) +days_since_epoch=$((today_days-epoch_days)) +echo $days_since_epoch diff --git a/Chapter_03/epoch_seconds b/Chapter_03/epoch_seconds new file mode 100644 index 0000000..5bfc393 --- /dev/null +++ b/Chapter_03/epoch_seconds @@ -0,0 +1,40 @@ +#!/bin/sh + +# +# This script outputs the number of seconds since 1/1/1970 +# +seconds_gnu_epoch=`date +'%s'` + +# This is the equation that works for the Gregorian calendar for years from +# 1582 through 9999. If you need to calculate something else, you'll need +# a new equation. +#Date_days=(Year*365)+(Year/4)-(Year/100)+(Year/400)+(Month*306001/10000)+(Day) +# If the month is greater than 2, add 1 to the month. Otherwise, for Jan/Feb, +# add 13 to the month and subtract 1 from the year. The following is the +# equation for Jan 1, 1970. +epoch_days=$(((1969*365)+(1969/4)-(1969/100)+(1969/400)+(14*306001/10000)+1)) + +hour=`date +'%k'` +minute=`date +'%M'` +second=`date +'%S'` + +day=`date +'%d'` +month=`date +'%m'` +year=`date +'%Y'` + +if [ $month -gt 2 ] +then + month=$(($month+1)) +else + month=$(($month+13)) + year=$(($year-1)) +fi + +today_days=$((($year*365)+($year/4)-($year/100)+($year/400)+($month*306001/10000)+$day)) +days_since_epoch=$(($today_days-$epoch_days)) +seconds_since_epoch=`echo "($days_since_epoch*86400)+($hour*3600)+($minute*60)+$second" | bc` +# There will likely be a difference in the following two values. The GNU +# time in seconds is calculated based on GMT instead of the timezone you +# are currently in. Mine, being pacific time is currently calculating with +# a difference of exactly 8 hr. +echo $seconds_since_epoch diff --git a/Chapter_03/time2notify b/Chapter_03/time2notify new file mode 100644 index 0000000..fab64ee --- /dev/null +++ b/Chapter_03/time2notify @@ -0,0 +1,40 @@ +#!/bin/sh + +# +# This script is the basic structure for performing or not performing a +# task based on the hour of the day or the day of the week. +# The endpoints in the range are inclusive. 0-6 and 0-23 are will notify +# all the time. +# +if [ $# -ne 4 ] +then + echo Usage: $0 {day begin} {day end} {hour begin} {hour end} + echo " Days are 0-6 where 0 is Sunday." + echo " Hours are 0-23." + exit +fi + +DAY_BEGIN=$1 +DAY_END=$2 +HOUR_BEGIN=$3 +HOUR_END=$4 + +# +# Setup variables for current day and hour for paging times +# + +# day of week 0-6 where 0=Sunday +DAY=`date +%w` +# hour of day in the form of 0-23 +HOUR=`date +%H` + +if [ $DAY -ge $DAY_BEGIN -a $DAY -le $DAY_END -a $HOUR -ge $HOUR_BEGIN -a $HOUR -le $HOUR_END ] +then + # traditionally a good return or in this case, a yes. + echo "It is time to notify" + echo 0 +else + # traditionally a bad return or in this case, a no. + echo "It is not time to notify" + echo 1 +fi diff --git a/Chapter_05/adm_opts b/Chapter_05/adm_opts new file mode 100644 index 0000000..7106cf5 --- /dev/null +++ b/Chapter_05/adm_opts @@ -0,0 +1,51 @@ +# +# a function that is sourced into your environment that will +# perform certain tasks based on the switches it is passed. +# + +jkl () { + + Usage="Usage: \n \ + \tjkl [-hlkntdx] \n \ + \t\t[-h] \tThis usage text.\n \ + \t\t[-l] \tGo to system log directory with ls. \n \ + \t\t[-k] \tDisplay disk utilization \n \ + \t\t[-n] \tDisplay network connectivity via less\n \ + \t\t[-t] \tDisplay top cpu consuming processes \n \ + \t\t[-d] \tTurn on debug (set -x) information.\n \ + \t\t[-x] \tTurn off debug (set +x) information.\n" + UNAME=`uname -n` + DATE=`date '+%y%m'` + + if [ "$#" -lt 1 ] + then + echo -e $Usage + fi + + OPTIND=1 + while getopts hlkntdx ARGS + do + case $ARGS in + l) if [ -d /var/log ] ; then + cd /var/log + /bin/ls + fi + ;; + k) df -k + ;; + n) netstat -a | less + ;; + t) ps -eo user,pid,ppid,pcpu,cmd | sort -rn +3 | head -n 5 + ;; + d) set -x + ;; + x) set +x + ;; + h) echo -e $Usage + ;; + *) echo -e $Usage + #return + ;; + esac + done +} diff --git a/Chapter_05/myapp.sh b/Chapter_05/myapp.sh new file mode 100644 index 0000000..70e2db9 --- /dev/null +++ b/Chapter_05/myapp.sh @@ -0,0 +1,3 @@ +MAJOR_VER=4 +MINOR_VER=3 +DOT_VER=15 diff --git a/Chapter_05/use_opts b/Chapter_05/use_opts new file mode 100644 index 0000000..51fe38b --- /dev/null +++ b/Chapter_05/use_opts @@ -0,0 +1,91 @@ + +# +# a function to be sourced into your shell environment that performs +# various tasks based on the switches it is passed. +# + +APPHOME=/home/rbpeters/scripts/apphome + +if [ ! -f $APPHOME/myapp.sh ] +then + echo "Myapp is not installed on this system so jkl is not functional" + return 1 +fi + +jkl () { + + Usage="Usage: \n \ + \t$0 [-lf:bmcdxh] \n \ + \t\t[-h] \tThis usage text.\n \ + \t\t[-f] \tcat specified file. \n \ + \t\t[-l] \tGo to application log directory with ls. \n \ + \t\t[-b] \tGo to application bin directory. \n \ + \t\t[-c] \tGo to application config directory.\n \ + \t\t[-m] \tGo to application log directory and more log file.\n \ + \t\t[-d] \tTurn on debug information.\n \ + \t\t[-x] \tTurn off debug information.\n" + LOG=log + CFG=config + BIN=bin + APPLOG=myapp_log + UNAME=`uname -n` + DATE=`date '+%y%m'` + MYAPP_ID=$APPHOME/myapp.sh + ###################################################################### + major=`egrep "^MAJOR_VER=" $MYAPP_ID | cut -d"=" -f2` + minor=`egrep "^MINOR_VER=" $MYAPP_ID | cut -d"=" -f2` + dot=`egrep "^DOT_VER=" $MYAPP_ID | cut -d"=" -f2` + ###################################################################### + APPDIR=$APPHOME/myapp.$major.$minor.$dot + LOGDIR=$APPHOME/myapp.$major.$minor.$dot/log + CFGDIR=$APPHOME/myapp.$major.$minor.$dot/config + BINDIR=$APPHOME/myapp.$major.$minor.$dot/bin + ###################################################################### + + if [ "$#" -lt 1 ] + then + echo -e $Usage + fi + + OPTIND=1 + while getopts lf:bmcdxh ARGS + do + case $ARGS in + l) if [ -d $LOGDIR ] ; then + cd $LOGDIR + /bin/ls + fi + ;; + f) FILE=$OPTARG + if [ -f $FILE ] + then + cat $FILE + else + echo $FILE not found. Please try again. + fi + ;; + b) if [ -d $BINDIR ] ; then + cd $BINDIR + fi + ;; + m) if [ -d $LOGDIR ] ; then + cd $LOGDIR + /bin/more $APPLOG + fi + ;; + c) if [ -d $CFGDIR ] ; then + cd $CFGDIR + fi + ;; + d) set -x + ;; + x) set +x + ;; + h) echo -e $Usage + ;; + *) echo -e $Usage + #return + ;; + esac + done +} diff --git a/Chapter_07/logwatch b/Chapter_07/logwatch new file mode 100644 index 0000000..1526287 --- /dev/null +++ b/Chapter_07/logwatch @@ -0,0 +1,136 @@ +#!/bin/sh +# +# logwatch watches log files for specified strings and notifies +# +# This must run all the time and have a configurable sleep time between runs. +# It must run all the time because it holds the current and previous lenght +# of each monitored file in memory. +# +debug=0 +# +# get the filecount information +# +DELAY=10 +LOGCHKS="/home/rbpeters/scripts/testlog:somethinG%20dumb:rbp:warn" + +while : +do + LOGTOCHK_COUNT=0 + for LOGTOCHK in `echo $LOGCHKS` + do + # + # Get all the values from the config file + # + logfile=`echo $LOGTOCHK | cut -d: -f1` + strings="`echo $LOGTOCHK | awk -F: '{print $2}'`" + # Replace the %20's with spaces + strings="`echo $strings | sed -e \"s/%20/ /g\"`" + exceptions=`echo $LOGTOCHK | cut -d: -f3` + # Replace the %20's with spaces + exceptions="`echo $exceptions | sed -e \"s/%20/ /g\"`" + notify=`echo $LOGTOCHK | cut -d: -f4` + test $debug -gt 0 && echo "INFO: Logfile: $logfile Strings: $strings Exceptions: $exceptions Notify: $notify" + # + # LOGTOCHK_COUNT is the number of the entry in the config file. If there + # are 2 log files that are configured to be watched, this value will + # be 1 and 2. Makes watching the same log file multiple times unique. + # + LOGTOCHK_COUNT=`expr $LOGTOCHK_COUNT + 1` + # + # The LOGNAME is just the name of the log file with /'s and .'s replaced + # with _'s. Both the LOGNAME and LOGTOCHK_COUNT are used later to build + # on-the-fly (indirect) variable names. + # + LOGNAME=`echo $logfile | sed -e s/\\\//_/g` + LOGNAME=`echo $LOGNAME | sed -e s/\\\./_/g` + # + # Check to see if the line count of the file is null. This indicates + # that it is the first run through and the counters should be reset. + # + if [ "`eval echo '$COUNT'${LOGNAME}_$LOGTOCHK_COUNT`" = "" ] + then + eval BASE${LOGNAME}_$LOGTOCHK_COUNT=`wc -l $logfile | awk '{ print $1 }'` + fi + # + # Get the line count of the log file + # + if [ -f $logfile ] + then + eval COUNT${LOGNAME}_$LOGTOCHK_COUNT=`wc -l $logfile | awk '{ print $1 }'` + else + test $debug -gt 0 && echo "$logfile does not exist" + echo "You might consider putting in some notification to let you know" + echo "that the file doesn't exist" + fi + # + # If the count is greater than the base value, get the number of lines + # that is greater, tail those lines and egrep for the desired values. + # Also, remove any lines that have the exeptions defined. + # + if [ `eval echo '$COUNT'${LOGNAME}_$LOGTOCHK_COUNT` -gt `eval echo '$BASE'${LOGNAME}_$LOGTOCHK_COUNT` ] + then + LINES=`eval expr '$COUNT'${LOGNAME}_$LOGTOCHK_COUNT - '$BASE'${LOGNAME}_$LOGTOCHK_COUNT` + eval BASE${LOGNAME}_$LOGTOCHK_COUNT='$COUNT'${LOGNAME}_$LOGTOCHK_COUNT + + if [ "$exceptions" != "" ] + then + MSGS=`tail -$LINES $logfile | egrep -i "$strings" | egrep -iv "$exceptions"` + test $debug -gt 0 && echo "MSGS is $MSGS" + else + MSGS=`tail -$LINES $logfile | egrep -i "$strings"` + test $debug -gt 0 && echo "MSGS is $MSGS" + fi + + # + # If there are any messages found, send the notification based on the + # desired type, warn/error. + # + if [ ! -z "$MSGS" ] + then + if [ "$notify" != "error" ] + then + echo "Replace this with the warning notification code." + else + echo "Replace this with the error notification code." + fi + fi + # + # If the line count of the file is less than the base value (the value + # from the previous loop through the code), reset the base value. This + # is likely because the log just rolled and is now starting over. Also + # check for the strings since messages may be missed when the log rolls. + # + elif [ `eval echo '$COUNT'${LOGNAME}_$LOGTOCHK_COUNT` -lt `eval echo '$BASE'${LOGNAME}_$LOGTOCHK_COUNT` ] + then + if [ "$exceptions" != "" ] + then + MSGS=`egrep -i "\"$strings\"" $logfile | egrep -iv "$exceptions"` + test $debug -gt 0 && echo "MSGS is $MSGS" + else + MSGS=`egrep -i "$strings" $logfile` + test $debug -gt 0 && echo "MSGS is $MSGS" + fi + # + # If there are any messages found, send the notification based on the + # desired type, warn/error. + # + if [ ! -z "$MSGS" ] + then + if [ "$notify" != "error" ] + then + echo "Replace this with the warning notification code." + else + echo "Replace this with the error notification code." + fi + fi + # This resets the tracked size of the log if the log gets smaller + eval BASE${LOGNAME}_$LOGTOCHK_COUNT='$COUNT'${LOGNAME}_$LOGTOCHK_COUNT + # + # If there is no change in the file, do nothing + # + else + test $debug -gt 0 && echo "No change in size of $logfile" + fi + done + sleep $DELAY +done diff --git a/Chapter_08/bash_ptree b/Chapter_08/bash_ptree new file mode 100644 index 0000000..a50731b --- /dev/null +++ b/Chapter_08/bash_ptree @@ -0,0 +1,72 @@ +#!/bin/bash + +# +# Display process table in tree form +# + +if [ "$1" = "" ] +then + proc=1 +else + proc=$1 +fi + +main () { + +PSOUT=`ps -ef | grep -v "^UID" | sort -n -k2` +# This technique will work in ksh, but since there are going to be array +# subscripts larger than 1024, bash is the way to go. +#ps -ef | grep -v "^UID" | while read line +while read line +do + line=`echo "$line" | sed -e s/\>/\\\\\\>/g` + #echo $line + # works in ksh/pdksh as long as the subscript is below 1024.. here it is not + # bash works fine though. + #set -A process $line for a ksh script + declare -a process=( $line ) + pid=${process[1]} + owner[$pid]=${process[0]} + ppid[$pid]=${process[2]} + command[$pid]="`echo $line | awk '{for(i=8;i<=NF;i++) {printf "%s ",$i}}'`" + children[${ppid[$pid]}]="${children[${ppid[$pid]}]} $pid" +done </\\\\\\>/g` + #echo $line + # works in ksh/pdksh as long as the subscript is below 1024.. here it is not + # bash works fine though. + #set -A process $line for a ksh script + #declare -a process=( $line ) + set -A process $line + pid=${process[1]} + eval owner$pid=${process[0]} + eval ppid$pid=${process[2]} + eval command$pid="\"`echo $line | awk '{for(i=8;i<=NF;i++) {printf \"%s \",$i}}'`\"" + eval parent='$ppid'$pid + eval children$parent=\"'$children'$parent $pid\" +done < $PSFILE +pscount=`wc -l $PSFILE | awk '{print $1}'` +count=0 + +# This technique will work in ksh, but since there are going to be array +# subscripts larger than 1024, bash is the way to go. +while [ $count -le $pscount ] +do + #line=`tail -n +$count $PSFILE | head -n 1` + line=`tail +$count $PSFILE | head -1` +#echo $line + line=`echo "$line" | sed -e s/\>/\\\\\\>/g` + # works in ksh/pdksh as long as the subscript is below 1024.. here it is not + # bash works fine though. + #set -A process $line for a ksh script + #declare -a process=( $line ) + #set -A process $line + pid=`echo $line | awk '{print $2}'` + + eval owner$pid=\"`echo $line | awk '{print $1}'`\" + + eval ppid$pid=\"`echo $line | awk '{print $3}'`\" + eval command$pid="\"`echo $line | awk '{for(i=8;i<=NF;i++) {printf \"%s \",$i}}'`\"" + eval parent='$ppid'$pid + eval children$parent=\"'$children'$parent $pid\" + count=`echo $count+1 | bc` +done +#done < $PSFILE + +print_tree $proc "" + +} + +print_tree () { +id=$1 + +echo -ne "$2$id \c" +eval echo \"'$owner'$id '$command'$id\" + +if eval [ \"'$children'$id\" = "\"\"" ] +then + return +else + for child in `eval echo '$children'$id` + do + eval parent='$ppid'$child + if [ "$child" = "`eval echo '$children'$parent | awk '{print $NF}'`" ] + then + echo "$2 \\" + temp="$2 " + else + echo "$2|\\" + temp="$2| " + fi + print_tree $child "$temp" + done +fi + +} + +main +rm $PSFILE diff --git a/Chapter_13/boot_check b/Chapter_13/boot_check new file mode 100644 index 0000000..0139b4f --- /dev/null +++ b/Chapter_13/boot_check @@ -0,0 +1,28 @@ +#!/bin/ksh + +# +# Perform alternate tasks based on how the script is called +# Use soft links to change the value of $0 + +whatami=`basename $0` + +case $whatami in + root_check) + fs="/" + ;; + boot_check) + fs="/boot" + ;; + snap_check) + fs="/snapshot" + ;; + *) + echo "Don't know what to do. Exiting" + ;; +esac + +fs_total=`df -k $fs | tail -n 1 | awk '{print $2}'` +fs_used=`df -k $fs | tail -n 1 | awk '{print $3}'` +percent_used=$((100*$fs_used/$fs_total)) + +echo "$fs is at ${percent_used}% capacity" diff --git a/Chapter_13/root_check b/Chapter_13/root_check new file mode 100644 index 0000000..0139b4f --- /dev/null +++ b/Chapter_13/root_check @@ -0,0 +1,28 @@ +#!/bin/ksh + +# +# Perform alternate tasks based on how the script is called +# Use soft links to change the value of $0 + +whatami=`basename $0` + +case $whatami in + root_check) + fs="/" + ;; + boot_check) + fs="/boot" + ;; + snap_check) + fs="/snapshot" + ;; + *) + echo "Don't know what to do. Exiting" + ;; +esac + +fs_total=`df -k $fs | tail -n 1 | awk '{print $2}'` +fs_used=`df -k $fs | tail -n 1 | awk '{print $3}'` +percent_used=$((100*$fs_used/$fs_total)) + +echo "$fs is at ${percent_used}% capacity" diff --git a/Chapter_13/snap_check b/Chapter_13/snap_check new file mode 100644 index 0000000..0139b4f --- /dev/null +++ b/Chapter_13/snap_check @@ -0,0 +1,28 @@ +#!/bin/ksh + +# +# Perform alternate tasks based on how the script is called +# Use soft links to change the value of $0 + +whatami=`basename $0` + +case $whatami in + root_check) + fs="/" + ;; + boot_check) + fs="/boot" + ;; + snap_check) + fs="/snapshot" + ;; + *) + echo "Don't know what to do. Exiting" + ;; +esac + +fs_total=`df -k $fs | tail -n 1 | awk '{print $2}'` +fs_used=`df -k $fs | tail -n 1 | awk '{print $3}'` +percent_used=$((100*$fs_used/$fs_total)) + +echo "$fs is at ${percent_used}% capacity" diff --git a/Chapter_14/bash_urlvalidator b/Chapter_14/bash_urlvalidator new file mode 100644 index 0000000..f68540d --- /dev/null +++ b/Chapter_14/bash_urlvalidator @@ -0,0 +1,137 @@ +#!/bin/bash +#set -x + +# +# Validate URL's that are found based on a top level URL that is passed +# to the script +# + +read_pipe=/tmp/read_pipe +write_pipe=/tmp/write_pipe + +for file in $read_pipe $write_pipe +do + if [ ! -p $file ] + then + mkfifo $file + fi +done + +LOGFILE=/tmp/valid_url.log +if [ -f $LOGFILE ] +then + rm $LOGFILE +fi + +function url_feeder +{ + for url in $* + do + set -m + echo $url > $write_pipe & + wait + set +m + done +} + +function find_urls +{ + url=$1 + urls=`lynx -dump $url | sed -n '/^References/,$p' | egrep -v "ftp://|javascript:|mailto:|news:|https://|file://|links" | tail -n +3 | awk '{print $2}' | cut -d\? -f1 | sort -u` + + urlcount=`echo $urls | wc -w` + + if [ "$urls" = "" ] + then + if [ "$2" != "" ] + then + echo $url Link Not Found on Server or Forbidden >> $LOGFILE + else + echo "" + fi + elif [ $urlcount -eq 1 -a "$urls" = "http://www.com.org/home.php" ] + then + if [ "$2" != "" ] + then + echo $url Site Not Found >> $LOGFILE + else + echo "" + fi + else + if [ "$2" != "" ] + then + echo "$url is valid" >> $LOGFILE + else + echo " $urls" + fi + fi +} + +first_url=$1 + +OPTIND=1 +while getopts l:u:p: ARGS +do + case $ARGS in + l) levels=$OPTARG + ;; + u) totalurls=$OPTARG + ;; + p) max_parallel=$OPTARG + ;; + *) echo "Usage: $0 -l [levels to look] -u [url] -p [parallel checks]" + exit + ;; + esac +done + +while [ $levels -ne 0 ] +do + (( levels -= 1 )) + for url in $totalurls + do + totalurls="${totalurls}`find_urls $url`" + url_count=`echo $totalurls | wc -w` + echo Current total number of urls is: $url_count + done +done + +totalurls=`for url in $totalurls +do + echo $url +done | sort -u` + +url_count=`echo $totalurls | wc -w` +echo Final unique total number of urls is: $url_count + +url_feeder $totalurls & +#coprocess_pid=$! + +processed_urls=0 +parallel_jobs=0 + +#echo "GO" > $read_pipe + +while [ $processed_urls -lt $url_count ] +do + unset parallel_pids + while [ $parallel_jobs -lt $max_parallel ] + do + if [ $(($processed_urls+$parallel_jobs)) -ge $url_count ] + then + break + fi + read url < $write_pipe +sleep .05s +echo The current url is $url + find_urls $url v & + parallel_pids="$parallel_pids $!" + parallel_jobs=$(($parallel_jobs+1)) + done + wait $parallel_pids + processed_urls=$(($processed_urls+$parallel_jobs)) + echo Processed $processed_urls URLs + parallel_jobs=0 +done + +rm $read_pipe $write_pipe diff --git a/Chapter_14/ksh_urlvalidator b/Chapter_14/ksh_urlvalidator new file mode 100644 index 0000000..8e56308 --- /dev/null +++ b/Chapter_14/ksh_urlvalidator @@ -0,0 +1,124 @@ +#!/bin/ksh + +# +# Validate links that are found from a passed top level URL +# + +LOGFILE=/tmp/valid_url.log +if [ -f $LOGFILE ] +then + rm $LOGFILE +fi + +function url_feeder +{ + while read + do + [[ $REPLY = "GO" ]] && break + done + + for url in $* + do + print $url + done +} + +function find_urls +{ + url=$1 + urls=`lynx -dump $url | sed -n '/^References/,$p' | egrep -v "ftp://|javascript:|mailto:|news:|https://" | tail -n +3 | awk '{print $2}' | cut -d\? -f1 | sort -u` + + urlcount=`echo $urls | wc -w` + + if [ "$urls" = "" ] + then + if [ "$2" != "" ] + then + echo $url Link Not Found on Server or Forbidden >> $LOGFILE + else + echo "" + fi + elif [ $urlcount -eq 1 -a "$urls" = "http://www.com.org/home.php" ] + then + if [ "$2" != "" ] + then + echo $url Site Not Found >> $LOGFILE + else + echo "" + fi + else + if [ "$2" != "" ] + then + echo "$url is valid" >> $LOGFILE + else + echo " $urls" + fi + fi +} + +first_url=$1 + +OPTIND=1 +while getopts l:u:p: ARGS +do + case $ARGS in + l) levels=$OPTARG + ;; + u) totalurls=$OPTARG + ;; + p) max_parallel=$OPTARG + ;; + *) echo "Usage: $0 -l [levels to look] -u [url] -p [parallel checks]" + exit + ;; + esac +done + +while [ $levels -ne 0 ] +do + (( levels -= 1 )) + for url in $totalurls + do + totalurls="${totalurls}`find_urls $url`" + url_count=`echo $totalurls | wc -w` + echo Current total number of urls is: $url_count + done +done + +totalurls=`for url in $totalurls +do + echo $url +done | sort -u` + +url_count=`echo $totalurls | wc -w` +echo Final unique total number of urls is: $url_count + +url_feeder $totalurls |& +coprocess_pid=$! + +processed_urls=0 +parallel_jobs=0 + +print -p "GO" + +while [ $processed_urls -lt $url_count ] +do + unset parallel_pids + while [ $parallel_jobs -lt $max_parallel ] + do + if [ $(($processed_urls+$parallel_jobs)) -ge $url_count ] + then + break + fi + read -p url + find_urls $url v & + parallel_pids="$parallel_pids $!" + parallel_jobs=$(($parallel_jobs+1)) + done + wait $parallel_pids + processed_urls=$(($processed_urls+$parallel_jobs)) + echo Processed $processed_urls URLs + parallel_jobs=0 +done + +wait $coprocess_pid diff --git a/Chapter_17/termserv_info b/Chapter_17/termserv_info new file mode 100644 index 0000000..d1e3a07 --- /dev/null +++ b/Chapter_17/termserv_info @@ -0,0 +1,78 @@ +#!/bin/sh + +# +# Called with either predefined switches or a custom command to send +# to a proprietary serial terminal server. This script calls a secondary +# expect script that does the interactive work. +# + +NODE="" +CMDS="" +NODEFILE="" +USAGE="There is a problem with the command, type $0 -h for syntax" + +if [ $# -eq 0 ] +then + echo $USAGE + exit 1 +fi + +OPTIND=1 +while getopts ihlc:f:n: opt +do + case $opt in + i) CMDS="$CMDS \"sho ip\"" + ;; + c) CUSTOM_CMD=$OPTARG + CMDS="$CMDS \"$CUSTOM_CMD\"" + ;; + h) cat << EOT +Usage: + $0 [-ihl] [-c "custom command"] [-f node_file] [-n node] + where: + -i Sends the "sho ip" command to the Xyplex terminal server + -h Displays this information + -l Logs out ports 1-15 + -c Takes a custom command and sends it to the xyplex. Double + quotes are required. You can have as many of these as + you like. + -f Define a file with a list of terminal servers to apply the + commands to. + -n Define a specific node to apply the command to. +EOT + exit 0 + ;; + l) CMDS="$CMDS \"logout por 1-15\"" + ;; + f) NODEFILE=$OPTARG + ;; + n) NODE=$OPTARG + ;; + ?) echo $USAGE + exit 1 + ;; + *) echo $USAGE + exit 1 + ;; + esac +done + +if [ "$NODEFILE" != "" ] +then + if [ -s $NODEFILE ] + then + for node in `cat $NODEFILE` + do + eval ./xyp_connect $node $LOGNAME $CMDS + done + else + echo There is a problem with $NODEFILE + fi +else + if [ "$NODE" != "" ] + then + eval ./xyp_connect $NODE $LOGNAME $CMDS + else + echo $USAGE + fi +fi diff --git a/Chapter_17/xyp_connect b/Chapter_17/xyp_connect new file mode 100644 index 0000000..647d2c1 --- /dev/null +++ b/Chapter_17/xyp_connect @@ -0,0 +1,55 @@ +#!/usr/bin/expect -f + +# +# Perform the interactive processing with expect to the terminal server. +# + +set TERMSERV [lindex $argv 0] +set USER [lindex $argv 1] + +send_user "These are the args $argv\n" + +# Start the session +catch {spawn -noecho telnet $TERMSERV 2000} + +set timeout 10 +expect { + timeout { send_user "Telnet timed out waiting for $TERMSERV\n" ; exit } + "onnection refused" { send_user "Connection was refused to $TERMSERV\n" ; exit } + "nknown host" { send_user "System $TERMSERV is unknown\n" ; exit} + "Escape character is '^]'." +} +send "\r" + +# Login +expect "#" { send "access\r" } +expect "username>" { send "$USER\r" } +expect ">" { send "set priv\r" } +expect "Password>" { send "system\r" } +expect ">>" + +# Send all the CMDS +set argc [llength $argv] +for {set i 2} {$i<$argc} {incr i} { + send "[lindex $argv $i]\r" + expect ">>" +} + +# Quit the session +send "" +expect "telnet>" +send "quit\r" +send_user "\n" + +#expect { + #"login: " { + #send "$USER\r" + #interact + #} "Username: " { + #send "$USER\r" + #interact + #} "Password: " + #interact + #} +#} +#interact diff --git a/Chapter_18/buildit b/Chapter_18/buildit new file mode 100644 index 0000000..513036c --- /dev/null +++ b/Chapter_18/buildit @@ -0,0 +1,14 @@ +#!/bin/sh + +# +# The main script for backgrounding a timeout value that will perform +# some task after the timeout has elapsed. +# + +HOMEDIR=$HOME/scripts + +$HOMEDIR/killit & +$HOMEDIR/readit +ans=$? + +echo The return code is: $ans diff --git a/Chapter_18/killit b/Chapter_18/killit new file mode 100644 index 0000000..dcb6b57 --- /dev/null +++ b/Chapter_18/killit @@ -0,0 +1,13 @@ +#!/bin/sh + +# +# Kill the process that is waiting on user interaction if the timeout +# has been reached +# + +readit_pid=`ps -ef | grep readit | grep -v grep | awk '{print $2}'` +sleep 3 +if [ "$readit_pid" != "" ] +then + kill -9 $readit_pid +fi diff --git a/Chapter_18/readit b/Chapter_18/readit new file mode 100644 index 0000000..dec0748 --- /dev/null +++ b/Chapter_18/readit @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Wait for user input +# + +echo Timeout in 3 seconds... +echo -e "Do you want to skip or not? (y or n are valid):" +read ans +ans=`echo $ans | tr "[A-Z]" "[a-z]"` +if [ "$ans" = "y" ] +then + exit 1 +else + exit 2 +fi diff --git a/Chapter_18/timeout b/Chapter_18/timeout new file mode 100644 index 0000000..5193232 --- /dev/null +++ b/Chapter_18/timeout @@ -0,0 +1,20 @@ +#!/bin/sh +#set -x + +# +# A more simple script to wait for user input and timeout after a predefined +# timeout has been reached. +# + +/usr/bin/echo -n "Input a letter or wait 3 seconds: " +# Min is the minimum number of characters for a completed read +# time is the time to delay before it continues. This is in tenths of seconds +# -icanon will disable the erase, kill, werase and rprnt special characters +stty -icanon min 0 time 30 +# Set the value of the 1 character that you typed to the ANSWER variable. +ANSWER=`dd bs=1 count=1 2>/dev/null` +# re-enable the erase, kill, werase and rprnt special characters +stty icanon +# two echos because it needs a carriage return after inputting the single +# character. +echo ; echo Answer: $ANSWER diff --git a/Chapter_19/hot_keys b/Chapter_19/hot_keys new file mode 100644 index 0000000..f1f8b0c --- /dev/null +++ b/Chapter_19/hot_keys @@ -0,0 +1,84 @@ +#!/bin/sh + +# +# Detect a user keypress ("hot keyboard") and perform the appropriate task. +# + +pi=`echo "a(1)*4" | bc -l` + +old_tty_settings=$(stty -g) # Save old settings. +stty -icanon +theta=0 +velocity=0 +distance= +height= +time= + +while : +do + + # put the eqations here as it should be run each loop +#set -x +# convert the angle from degrees to radians +angle=`echo "scale=5;$theta/57.29578" | bc -l` +# gravity is 9.8m/s^2 +distance=`echo "scale=5;(($velocity^2)*2*(s($angle))*(c($angle)))/9.8" | bc -l` +height=`echo "scale=5;(($velocity*s($angle))^2/(2*9.8))" | bc -l` +time=`echo "scale=5;(2*($velocity*s($angle))/(9.8))" | bc -l` +#set +x + + clear + echo "j to decrease launch angle --- k to increase launch angle" + echo "h to decrease launch velocity --- l to increase launch velocity" + echo + echo "x or q to exit." + echo + echo "Launch angle deg.=$theta Velocity M/s=$velocity" + echo + echo "Distance: $distance meters" + echo "Maximum height: $height meters" + echo "Flight Time: $time seconds" + + Keypress=$(head -c1) # or $(dd bs=1 count=1 2> /dev/null) + # on non-GNU systems + + # The velocity won't go below zero and the angle will roll between 0 and + # 90 and then back to zero again. + case $Keypress in + j|J) + if [ $theta -ne 0 ] + then + theta=$(($theta-1)) + else + theta=90 + fi + ;; + k|K) + if [ $theta -ne 90 ] + then + theta=$(($theta+1)) + else + theta=0 + fi + ;; + h|H) + if [ $velocity -ne 0 ] + then + velocity=$(($velocity-1)) + else + velocity=0 + fi + ;; + l|L) + velocity=$(($velocity+1)) + ;; + q|Q|x|X) + break + ;; + esac + +done + +stty "$old_tty_settings" # Restore old settings. + +exit 0 diff --git a/Chapter_20/dircp b/Chapter_20/dircp new file mode 100644 index 0000000..354268f --- /dev/null +++ b/Chapter_20/dircp @@ -0,0 +1,22 @@ +#!/bin/sh + +# +# Copy a whole directory while maintaining the as many attributes of the +# original directory such as ownership and permissions. +# + +if [ $# -ne 2 ] +then + echo Usage: $0 {Source Directory} {Destination Directory} + exit 1 +fi + +SRC=$1 +DST=$2 + +if [ ! -d $DST ] +then + mkdir -p $DST +fi + +find $SRC -depth | xargs tar cvf - | (cd $DST && tar xvfp -) diff --git a/Chapter_21/profile b/Chapter_21/profile new file mode 100644 index 0000000..2c5b4ca --- /dev/null +++ b/Chapter_21/profile @@ -0,0 +1,20 @@ + +# +# System profile segment to grab and save the Xauthority information +# + +if [ "`uname -n`" = "casper" ] && [ "$REMOTEHOST" != "casper" ] && [ "$DISPLAY" != "" ] +then + xauth nextract - $DISPLAY > $HOME/.xauth.$LOGNAME + chmod 600 $HOME/.xauth.$LOGNAME + echo $DISPLAY > $HOME/.xdisp.$LOGNAME + chmod 600 $HOME/.xdisp.$LOGNAME + ~/scripts/window.sh +else + if [ -f $HOME/.xauth.$LOGNAME ] + then + cat $HOME/.xauth.$LOGNAME | xauth nmerge - + DISPLAY=`cat $HOME/.xdisp.$LOGNAME` + export DISPLAY + fi +fi diff --git a/Chapter_21/root_profile b/Chapter_21/root_profile new file mode 100644 index 0000000..c263f5b --- /dev/null +++ b/Chapter_21/root_profile @@ -0,0 +1,28 @@ + +# +# root profile segment for root to put the Xauthority values into its +# environment to be able display x clients +# + +XAUTH=`which xauth` +MYPPID=`ps -fp $$ | tail -n 1 | awk '{print $3}'` +MYPPPID=`ps -fp $MYPPID | tail -n 1 | awk '{print $3}'` +MYID=`ps -fp $MYPPID | tail -n 1 | awk '{print $1}'` +MYOID=`ps -fp $MYPPPID | tail -n 1 | awk '{print $1}'` +if [ "$MYID" != "root" ] || [ "$MYOID" != "root" ] +then + for user in $MYID $MYOID + do + if [ "$user" != "root" ] + then + MYID=$user + fi + done + MYHOME=`grep "^$MYID:" /etc/passwd | cut -d: -f6` + if [ -f $MYHOME/.xauth.$MYID ] + then + cat $MYHOME/.xauth.$MYID | $XAUTH nmerge - + DISPLAY=`cat $MYHOME/.xdisp.$MYID` + export DISPLAY + fi +fi diff --git a/Chapter_21/throw_ssh_root b/Chapter_21/throw_ssh_root new file mode 100644 index 0000000..57e2742 --- /dev/null +++ b/Chapter_21/throw_ssh_root @@ -0,0 +1,53 @@ +#!/bin/sh +#set -x + +# +# Throw a root window to a users desktop display +# + +if [ $# -ne 1 ] +then + echo "This script will send a root window to a users display that is using" + echo "ssh as there access to the environment" + echo + echo "Usage: $0 {username}" + exit 1 +fi + +user=$1 +userhome=`grep "^$user:" /etc/passwd | cut -d: -f6` +if [ -f $userhome/.xdisp.$user ] +then + if [ -f /usr/bin/X11/xauth ] + then + cat $userhome/.xauth.$user | /usr/bin/X11/xauth nmerge - + if [ $? -ne 0 ] + then + echo "Something wrong with the $userhome/$user/.xauth file" + echo "Please fix and try again." + exit 1 + fi + else + cat $userhome/.xauth.$user | /usr/openwin/bin/xauth nmerge - + fi + DISPLAY=`cat $userhome/.xdisp.$user` + export DISPLAY +else + echo "SSH Display information not available for $user. Is $user using it?" + exit 1 +fi + +RIP="This `whoami`@`uname -n` xterm window expires in 1 hr from `date '+%H:%M'`" + +if [ -f /usr/bin/X11/xterm ] +then + nohup /usr/bin/X11/xterm -ls -sb -T "$RIP" & +elif [ -f /usr/openwin/bin/xterm ] +then + nohup /usr/openwin/bin/xterm -ls -sb -T "$RIP" & +else + nohup /usr/bin/xterm -ls -sb -T "$RIP" & +fi + +PID=$! +echo "kill $PID" | at -m now + 1 hour diff --git a/Chapter_22/where.sh b/Chapter_22/where.sh new file mode 100644 index 0000000..32060f8 --- /dev/null +++ b/Chapter_22/where.sh @@ -0,0 +1,93 @@ +#!/bin/sh + +# +# Read the input of the user for the system they want to attach to and +# start an x client that contains the connection to that machine as well +# as using the type specified. +# + +CONFIG_FILE=$HOME/.whererc.$LOGNAME +LOG_FILE=$HOME/.whererc.${LOGNAME}.log + +stty intr '' kill '' +stty erase '' + +while true +do + if [ -f /usr/ucb/echo ] + then + /usr/ucb/echo -n "Node: " + elif [ -f /bin/echo ] + then + /bin/echo -n "Node: " + else + /usr/bin/echo "Node: " + fi + + read nodename conn + if [ "$nodename" = "" ] + then + continue + fi + + nodename=`echo $nodename | tr "[A-Z]" "[a-z]"` + if [ -f $CONFIG_FILE ] + then + . $CONFIG_FILE + fi + + if [ "$CONNECTION_TYPE" = "any" ] + then + # These commands timeout in about 2 seconds each.. + S=`nmap -p 22 --max_rtt_timeout 500 $nodename | grep open` + R=`nmap -p 513 --max_rtt_timeout 500 $nodename | grep open` + if [ "$S" != "" ] + then + CONNECTION_TYPE=ssh + elif [ "$R" != "" ] + then + CONNECTION_TYPE=rlogin + else + CONNECTION_TYPE=telnet + fi + fi + + if [ "$conn" != "" ] + then + case $conn in + r) # Use rlogin + CONNECTION_TYPE=rlogin + ;; + s) # Use ssh + CONNECTION_TYPE=ssh + ;; + t) # Use telnet + CONNECTION_TYPE=telnet + ;; + *) # Do nothing + echo + ;; + esac + fi + + echo `date` $nodename $CONNECTION_TYPE $conn >> $LOG_FILE + third_ip=`grep -w $nodename /etc/hosts | grep -v '^#' | tail -n 1 | awk '{print $1}' | cut -d\. -f3` + if [ "$third_ip" = "" ] + then + third_ip=`echo $nodename | awk -F. '{print $3}'` + if [ "$third_ip" = "" ] + then + nohup $XTERM -fn $FONT -bg $OTHER_BG -fg $OTHER_FG -sb -sl 500 -T "$nodename" -e "$CONNECTION_TYPE" -l $USER $nodename >/dev/null & + continue + fi + fi + if [ $third_ip -ge 1 -a $third_ip -le 10 ] + then + nohup $XTERM -fn $FONT -bg $PROD_BG -fg $PROD_FG -sb -sl 500 -T "$nodename" -e "$CONNECTION_TYPE" -l $USER $nodename >/dev/null & + elif [ $third_ip -ge 11 -a $third_ip -le 20 ] + then + nohup $XTERM -fn $FONT -bg $NON_PROD_BG -fg $NON_PROD_FG -sb -sl 500 -T "$nodename" -e "$CONNECTION_TYPE" -l $USER $nodename >/dev/null & + else + nohup $XTERM -fn $FONT -bg $OTHER_BG -fg $OTHER_FG -sb -sl 500 -T "$nodename" -e "$CONNECTION_TYPE" -l $USER $nodename >/dev/null & + fi +done diff --git a/Chapter_22/window.sh b/Chapter_22/window.sh new file mode 100644 index 0000000..4764bc1 --- /dev/null +++ b/Chapter_22/window.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +# +# Display the small window that receives the input to connect to other +# remote machines. Set up the user configuration if it doesn't already +# exist. +# + +CONFIG_FILE=$HOME/.whererc.$LOGNAME + +RLOGIN_TITLE="Where..." +RLOGIN_FG=red +RLOGIN_BG=ivory + +if [ -f $HOME/.whererc ] +then + mv $HOME/.whererc $CONFIG_FILE + . $CONFIG_FILE +elif [ -f $CONFIG_FILE ] +then + . $CONFIG_FILE +else + cat > $CONFIG_FILE <> $CONFIG_FILE + changes=1 + fi +done + +if [ -f $CONFIG_FILE -a $changes -eq 1 ] +then + . $CONFIG_FILE +fi + +nohup $XTERM -cr $RLOGIN_FG -fg $RLOGIN_FG -bg $RLOGIN_BG -fn 12x24 -rw -geom $WHERE_WIN_GEOM -T "$RLOGIN_TITLE" -ls -e ~/scripts/where.sh >/dev/null & diff --git a/Chapter_23/mime_attach b/Chapter_23/mime_attach new file mode 100644 index 0000000..c7c2276 --- /dev/null +++ b/Chapter_23/mime_attach @@ -0,0 +1,59 @@ +#!/bin/sh + +# +# Send an email with an attachment using mime encoding from the command line. +# + +tmpfile=/tmp/mime_output.$$ + +from="$USER@`cat /etc/dnsdomainname`" +usage="Usage: $0 {filename} {email address}" + +if [ ! -z $1 ] +then + file=$1 + if [ ! -f $file ] + then + echo $usage + exit 1 + else + if [ ! -z $2 ] + then + address=$2 + else + echo $usage + exit 1 + fi + fi +else + echo $usage + exit 1 +fi + +basefile=`basename $file` + +cat > $tmpfile << EOT +From: $from +To: $address +Subject: $file attached from $from +MIME-Version: 1.0 +Content-Type: multipart/mixed;boundary="mime-attachment-boundary" + +--mime-attachment-boundary +Content-Type: text/plain; charset="iso-8859-1" +Content-Transfer-Encoding: 8bit + +A MIME encoded file is attached called: $basefile +--mime-attachment-boundary +Content-Type: application/octet-stream; name="$basefile" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename="$basefile" + +EOT +/usr/bin/mimencode $file >> $tmpfile + +echo --mime-attachment-boundary-- >> $tmpfile + +/usr/lib/sendmail -t < $tmpfile + +rm $tmpfile diff --git a/Chapter_23/uu_attach b/Chapter_23/uu_attach new file mode 100644 index 0000000..89815ec --- /dev/null +++ b/Chapter_23/uu_attach @@ -0,0 +1,40 @@ +#!/bin/bash + +# +# Send an email with an attachment using uuencoding from the command line. +# + +tmpfile=/tmp/uu_output.$$ + +usage="Usage: $0 {filename} {email_address}" + +if [ ! -z $1 ] +then + file=$1 + if [ ! -f $file ] + then + echo $usage + exit 1 + else + if [ ! -z $2 ] + then + address=$2 + else + echo $usage + exit 1 + fi + fi +else + echo $usage + exit 1 +fi + +basefile=`basename $file` + +echo "A uuencoded file is attached called: $basefile" > $tmpfile +echo >> $tmpfile +uuencode $file $file >> $tmpfile + +mail -s "$basefile attached from $from" $address < $tmpfile + +rm $tmpfile diff --git a/Chapter_27/reverse b/Chapter_27/reverse new file mode 100644 index 0000000..265096e --- /dev/null +++ b/Chapter_27/reverse @@ -0,0 +1,37 @@ +#!/bin/sh + +# +# Reverse the output of a file or stdin using a pseudo array of indirect +# variables. +# + +counter=0 + +populate () { + counter=$(($counter+1)) + eval COUNT${counter}='$LINE' +} + +if [ "$1" != "" ] +then + if [ -f $1 ] + then + while read LINE + do + populate + done < $1 + else + echo "$1 Does not exist, please try again" + fi +else + while read LINE + do + populate + done +fi + +while [ $counter -gt 0 ] +do + eval echo '$COUNT'$counter + counter=$(($counter-1)) +done diff --git a/Chapter_27/reverse2 b/Chapter_27/reverse2 new file mode 100644 index 0000000..eea66a7 --- /dev/null +++ b/Chapter_27/reverse2 @@ -0,0 +1,35 @@ +#!/bin/sh + +# +# Reverse the output of input, either a file or stdin using a single +# variable to hold the output. +# + +counter=0 + +populate () { + counter=$(($counter+1)) + eval COUNT${counter}='$LINE' +} + +if [ "$1" != "" ] +then + if [ -f $1 ] + then + while read LINE + do + #populate + reversed_var="$LINE $reversed_var" + done < $1 + else + echo "$1 Does not exist, please try again" + fi +else + while read LINE + do + #populate + reversed_var="$LINE $reversed_var" + done +fi + +echo $reversed_var diff --git a/Chapter_29/auto_ftp b/Chapter_29/auto_ftp new file mode 100644 index 0000000..9730f04 --- /dev/null +++ b/Chapter_29/auto_ftp @@ -0,0 +1,17 @@ +#!/bin/sh + +# A script to automatically download the latest squidguard blacklist + +SERVER=ftp.teledanmark.no +FILE=blacklists.tar.gz +DIR=/pub/www/proxy/squidGuard/contrib + +echo "FTPing current squidguard blacklist" +ftp -n $SERVER << EOF + user anonymous rbpeters@peterro.com + cd $DIR + ls -l + hash + bye +EOF + #get $FILE diff --git a/Chapter_30/00.procmailrc b/Chapter_30/00.procmailrc new file mode 100644 index 0000000..6a42bce --- /dev/null +++ b/Chapter_30/00.procmailrc @@ -0,0 +1,32 @@ +# +# .procmailrc: The procmail resource control file to configure how +# you want email to be processed by procmail. This file should be +# named .procmailrc +# +#------------------------------------------------------------- +PATH=/bin:/usr/bin:/usr/local/bin +MAILDIR=$HOME/Mail +LOGFILE=$MAILDIR/procmail.log +VERBOSE=yes +LOGABSTRACT=yes +SUBJECT=`formail -xSubject:` +FROM=`formail -xFrom:` +#------------------------------------------------------------- +# Grab mail messages and feed the body of the message to the pipe +# +# +# --- System Audits -------------------------------- +#:0 getthisfile.lock + #* ^Subject:.*getthisfile + #{ + #:0 b + #| /home/rbpeters/scripts/getthisfile + #} +#------------All other messages ------------------ +#:0 c +#* + #! ron.peters@mydomain.com + +#:0fw +#* < 256000 +#| /usr/bin/spamc -d localhost diff --git a/Chapter_30/getthisfile b/Chapter_30/getthisfile new file mode 100644 index 0000000..44f5ab9 --- /dev/null +++ b/Chapter_30/getthisfile @@ -0,0 +1,56 @@ +#!/bin/sh +#set -x + +# +# Simple script called by procmail to interact with a machine via email. +# + +warn_mail=my_email@mydomain.com +me=`uname -n` + +LOG=/home/$LOGNAME/scripts/filetoget.log +echo `date` >> $LOG +echo Subject: $SUBJECT >> $LOG + +filetoget=`echo $SUBJECT | awk '{print $3}'` +echo filetoget: $filetoget >> $LOG + +whattodo=`echo $SUBJECT | awk '{print $2}'` +echo whattodo: $whattodo >> $LOG + +command=`echo $SUBJECT | cut -d\ -f3-` +echo Command: $command >> $LOG + +if [ "$FROM" != " user1@good_domain.com" -a "$FROM" != " user2@another_good_domain.com" ] +then + echo "Invalid user $FROM trying to get procmail info: $SUBJECT" >> $LOG + tail -10 $LOG | mail -s "$FROM attempting to get procmail info" $warn_mail + exit 0 +fi + +if [ "$whattodo" = "binary" ] +then + echo "binary filetoget: $filetoget" >> $LOG + echo "sending it to: $FROM" >> $LOG + cd /tmp + filename=`echo $filetoget | awk -F/ '{print $NF}'` + uuencode $filetoget $filename > /tmp/$filename.uue + echo "cat /tmp/$filename.uue | mail -s \"uuencoded $filetoget from $me\" $FROM" >> $LOG + cat /tmp/$filename.uue | mail -s "uuencoded $filetoget from $me" $FROM >> $LOG + rm /tmp/$filename.uue +elif [ "$whattodo" = "command" ] +then + echo Running command $command >> $LOG + echo "$command | mail -s \"Output of $command on $me\" $FROM" >> $LOG + $command | mail -s "Output of $command on $me" $FROM >> $LOG +elif [ "$whattodo" = "help" ] +then + echo "Sending usage to $FROM" >> $LOG + echo "Subject: getthisfile {binary|command|help} {path/file|command}" | mail -s "Usage" $FROM +else + filetoget=$whattodo + echo "filetoget: $filetoget" >> $LOG + echo "sending it to: $FROM" >> $LOG + echo "cat $filetoget | mail -s \"$filetoget from $me\" $FROM" >> $LOG + cat $filetoget | mail -s "$filetoget from $me" $FROM >> $LOG +fi diff --git a/Chapter_31/kill_proc b/Chapter_31/kill_proc new file mode 100644 index 0000000..bbe7471 --- /dev/null +++ b/Chapter_31/kill_proc @@ -0,0 +1,179 @@ +#!/bin/ksh +# +# Kill processes based on their resource consumption. +# +# syntax: +# :::: +# +# where: +# cputime is in minutes +# age is in minutes +# pcpu is in percentage points +# size is in kilobytes +# +# notify options: +# 0: Notify mail/page on warn/error thresholds, DONT kill process +# NOTE: any other value will default to 0 (zero). +# 1: Notify mail/page on warn/error thresholds, AND kill process +# 2: Notify via mail on error threshold, AND kill process +# 3: Kill process without any notification. +# +debug=1 +sleeptime=3 +kill_plist="dhcpd:etime:1:27:1" +clear + +notify () +{ + case $2 in + 0) + # Warn/error level and don't kill.. + echo "$1: $3 process id $4 found with $5 $7. Should be less than $6." + ;; + 1) + # Warn/error level and kill.. + echo "$1: $3 process id $4 found with $5 $7. Should be less than $6." + test $debug -eq 0 && kill $4 + ;; + 2) + # Warning level only... + echo "Warning: $3 process id $4 found with $5 $7. Should be less than $6." + test $debug -eq 0 && kill $4 + ;; + 3) + # Just kill, don't warn at all.. + test $debug -eq 0 && kill $4 + ;; + esac +} + + +while : +do + for pline in $kill_plist + do + process=`echo $pline | cut -d: -f1` + process="`echo $process | sed -e \"s/%20/ /g\"`" + type=`echo $pline | cut -d: -f2` + value=`echo $pline | awk -F: '{print $3}'` + errval=`echo $pline | awk -F: '{print $4}'` + killoption=`echo $pline | awk -F: '{print $5}'` + if [ "$killoption" = "" ] + then + killoption=0 + fi + + test $debug -gt 0 && echo "Kill $process processes if $type is greater than $errval" + + for pid in `ps -eo pid,comm | egrep "${process}$|${process}:$" | grep -v grep | awk '{print $1}'` + do + test $debug -gt 0 && echo "$process pid $pid" + pid_string=`ps -eo pid,cputime,etime,pcpu,vsize,comm | grep $pid | egrep "${process}$|${process}:$" | grep -v grep` + case $type in + "cputime") + proc_time=`echo $pid_string | awk '{print $2}'` + fields=`echo $proc_time | awk -F: '{print NF}'` + proc_time_min=`echo $proc_time | awk -F: '{print $(NF-1)}'` + if [ $fields -lt 3 ] + then + proc_time_hr=0 + proc_time_day=0 + else + proc_time_hr=`echo $proc_time | awk -F: '{print $(NF-2)}'` + fields=`echo $proc_time_hr | awk -F- '{print NF}'` + if [ $fields -ne 1 ] + then + proc_time_day=`echo $proc_time_hr | awk -F- '{print $1}'` + proc_time_hr=`echo $proc_time_hr | awk -F- '{print $2}'` + else + proc_time_day=0 + fi + fi + curr_cpu_time=`echo "$proc_time_day*1440+$proc_time_hr*60+$proc_time_min" | bc` + test $debug -gt 0 && echo "Current cpu time for $process pid $pid is $curr_cpu_time minutes" + # + # Test the thresholds + # + if test $curr_cpu_time -gt $value -a $curr_cpu_time -lt $errval + then + notify "Warning" $killoption $process $pid $curr_cpu_time $value "minutes of CPU time" + elif test $curr_cpu_time -ge $errval + then + notify "Error" $killoption $process $pid $curr_cpu_time $value "minutes of CPU time" + else + test $debug -gt 0 && echo "process cpu time ok" + fi + ;; + "etime") + proc_age=`echo $pid_string | awk '{print $3}'` + fields=`echo $proc_age | awk -F: '{print NF}'` + proc_age_min=`echo $proc_age | awk -F: '{print $(NF-1)}'` + if [ $fields -lt 3 ] + then + proc_age_hr=0 + proc_age_day=0 + else + proc_age_hr=`echo $proc_age | awk -F: '{print $(NF-2)}'` + fields=`echo $proc_age_hr | awk -F- '{print NF}'` + if [ $fields -ne 1 ] + then + proc_age_day=`echo $proc_age_hr | awk -F- '{print $1}'` + proc_age_hr=`echo $proc_age_hr | awk -F- '{print $2}'` + else + proc_age_day=0 + fi + fi + curr_age=`echo "$proc_age_day*1440+$proc_age_hr*60+$proc_age_min" | bc` + test $debug -gt 0 && echo "Current age of $process pid $pid is $curr_age minutes" + # + # Test the thresholds + # + if test $curr_age -gt $value -a $curr_age -lt $errval + then + notify "Warning" $killoption $process $pid $curr_age $value "minutes of elapsed time" + elif test $curr_age -ge $errval + then + notify "Error" $killoption $process $pid $curr_age $value "minutes of elapsed time" + else + test $debug -gt 0 && echo "process age ok" + fi + ;; + "pcpu") + curr_proc_cpu=`echo $pid_string | awk '{print $4}' | awk -F. '{print $1}'` + test $debug -gt 0 && echo "Current percent cpu of $process pid $pid is $curr_proc_cpu" + # + # Test the thresholds + # + if test $curr_proc_cpu -gt $value -a $curr_proc_cpu -lt $errval + then + notify "Warning" $killoption $process $pid $curr_proc_cpu $value "percent of the CPU" + elif test $curr_proc_cpu -ge $errval + then + notify "Error" $killoption $process $pid $curr_proc_cpu $value "percent of the CPU" + else + test $debug -gt 0 && echo "process cpu percent ok" + fi + ;; + "vsize") + curr_proc_size=`echo $pid_string | awk '{print $5}'` + test $debug -gt 0 && echo "Current size of $process pid $pid is $curr_proc_size" + # + # Test the thresholds + # + if test $curr_proc_size -gt $value -a $curr_proc_size -lt $errval + then + notify "Warning" $killoption $process $pid $curr_proc_size $value "blocks of virtual size" + elif test $curr_proc_size -ge $errval + then + notify "Error" $killoption $process $pid $curr_proc_size $value "blocks of virtual size" + else + test $debug -gt 0 && echo "process virtual size ok" + fi + ;; + esac + done + done + sleep $sleeptime + clear +done diff --git a/Chapter_32/mon_files b/Chapter_32/mon_files new file mode 100644 index 0000000..d866a98 --- /dev/null +++ b/Chapter_32/mon_files @@ -0,0 +1,22 @@ +#!/bin/sh +set -x +# +# Monitor number of files in a directory +# + +FILENUM="/home/$LOGNAME/scripts/files:50:100:root /home/$LOGNAME/scripts:50:100:me@my_domain.net /:50:100:$LOGNAME" + +for monitor in $FILENUM +do + set -- `echo $monitor | sed -e 's/:/ /g'` + file_count=`ls $1 | wc -l` + if [ $file_count -lt $2 ] + then + continue + elif [ $file_count -ge $2 -a $file_count -lt $3 ] + then + echo "Warning: File count in $1 is $file_count, should be less than $2" | mail -s "Directory file count warning for $1" $4 + else + echo "Error: File count in $1 is $file_count, should be less than $2" | mail -s "Directory file count error for $1" $4 + fi +done diff --git a/Chapter_34/rcs_vi b/Chapter_34/rcs_vi new file mode 100644 index 0000000..47e283c --- /dev/null +++ b/Chapter_34/rcs_vi @@ -0,0 +1,40 @@ +#!/bin/sh +# +# Short script to take care of the ugliness of having to always checkout +# and check back in a file after editing it. Of course some basic +# assumptions are made: +# 1. You have RCS installed +# 2. No command line options are supplied to vi (or vim) +# + +# Setup the environment +CI=/usr/bin/ci +CO=/usr/bin/co +TEST=/usr/bin/test +BASENAME=/bin/basename +ME=`$BASENAME $0` +DIRNAME=/usr/bin/dirname +ALLTHEFILES=$@ + +# Your renamed vi (or vim) executable +VI=/usr/bin/vim-rcs + +# This list of files that were passed +for file in $ALLTHEFILES +do + # Get some basic info about the file + PATH=`$DIRNAME $file` + FILENAME=`$BASENAME $file` + $TEST -f "$PATH/RCS/$FILENAME,v" -o -f "$PATH/$FILENAME,v" && $CO -l $file + + # We still want to edit the file, whether its in RCS or not + $VI $file + + # A link to the same script to perform + if [ "$ME" = "vir" ] + then + $TEST ! -f "$PATH/RCS/$FILENAME,v" -o ! -f "$PATH/$FILENAME,v" && $CI -i $file + else + $TEST -f "$PATH/RCS/$FILENAME,v" -o -f "$PATH/$FILENAME,v" && $CI -u $file + fi +done diff --git a/Chapter_35/sysreport b/Chapter_35/sysreport new file mode 100644 index 0000000..801b7cc --- /dev/null +++ b/Chapter_35/sysreport @@ -0,0 +1,126 @@ +#!/bin/sh + +# +# Output a snapshot of the current system state using pretty colors +# + +def_colors () { +# Attributes + normal='\033[0m'; bold='\033[1m'; dim='\033[2m'; under='\033[4m' + italic='\033[3m'; noitalic='\033[23m'; blink='\033[5m'; + reverse='\033[7m'; conceal='\033[8m' nobold='\033[22m'; + nounder='\033[24m'; noblink='\033[25m' + # Foreground + black='\033[30m'; red='\033[31m'; green='\033[32m'; yellow='\033[33m' + blue='\033[34m'; magenta='\033[35m'; cyan='\033[36m'; white='\033[37m' + # Background + bblack='\033[40m'; bred='\033[41m'; bgreen='\033[42m'; byellow='\033[43m' + bblue='\033[44m'; bmagenta='\033[45m'; bcyan='\033[46m'; bwhite='\033[47m' +} +def_colors + +clear +hostname=`cat /proc/sys/kernel/hostname` +echo +echo -e "System Report for $white$hostname$normal on `date`" +echo + +nisdomain=`cat /proc/sys/kernel/domainname` +processor=`grep 'model name' /proc/cpuinfo | cut -d: -f2 | cut -c2-` +cache=`grep 'cache size' /proc/cpuinfo | awk '{print $4,$5}'` +bogomips=`grep 'bogomips' /proc/cpuinfo | awk '{print $3}'` +vendor=`grep 'vendor_id' /proc/cpuinfo` + +echo -e "Hostname: $white$hostname$normal NIS Domain: $white$nisdomain$normal" +if [ "`echo $vendor | grep -i intel`" ] +then + cpu_color=$blue +elif [ "`echo $vendor | grep -i amd`" ] +then + cpu_color=$green +fi + +echo -e "Processor: $cpu_color$processor$normal" +echo -e " Running at $white$bogomips$normal bogomips with $white$cache$normal cache" +echo + +ostype=`cat /proc/sys/kernel/ostype` +osrelease=`cat /proc/sys/kernel/osrelease` +rev=`cat /proc/sys/kernel/version | awk '{print $1}'` +da_date=`cat /proc/sys/kernel/version | cut -d\ -f2-` +upsec=`awk '{print $1}' /proc/uptime` +uptime=`echo "scale=2;$upsec/86400" | bc` + +echo -e "OS Type: $white$ostype$normal Kernel: $white$osrelease$normal" +echo -e " Kernel Compile $white$rev$normal on $white$da_date$normal" +echo -e "Uptime: $magenta$uptime$normal days" + +set `grep MemTotal /proc/meminfo` +tot_mem=$2 ; tot_mem_unit=$3 +set `grep MemFree /proc/meminfo` +free_mem=$2 ; fre_mem_unit=$3 +perc_mem_used=$((100-(100*free_mem/tot_mem))) +set `grep SwapTotal /proc/meminfo` +tot_swap=$2 ; tot_swap_unit=$3 +set `grep SwapFree /proc/meminfo` +free_swap=$2 ; fre_swap_unit=$3 +perc_swap_used=$((100-(100*free_swap/tot_swap))) + +if [ $perc_mem_used -lt 80 ] +then + mem_color=$green +elif [ $perc_mem_used -ge 80 -a $perc_mem_used -lt 90 ] +then + mem_color=$yellow +else + mem_color=$red +fi +if [ $perc_swap_used -lt 80 ] +then + swap_color=$green +elif [ $perc_swap_used -ge 80 -a $perc_swap_used -lt 90 ] +then + swap_color=$yellow +else + swap_color=$red +fi + +echo -e "Memory: $white$tot_mem$normal $tot_mem_unit Free: $white$free_mem$normal $fre_mem_unit %Used: $mem_color$perc_mem_used$normal" +echo -e "Swap: $white$tot_swap$normal $tot_swap_unit Free: $white$free_swap$normal $fre_swap_unit %Used: $swap_color$perc_swap_used$normal" +echo + +set `cat /proc/loadavg` +one_min=$1 +five_min=$2 +fifteen_min=$3 +echo -n "Load Average:" +for ave in $one_min $five_min $fifteen_min +do + int_ave=`echo $ave | cut -d. -f1` + if [ $int_ave -lt 1 ] + then + echo -en " $green$ave$normal" + elif [ $int_ave -ge 1 -a $int_ave -lt 5 ] + then + echo -en " $yellow$ave$normal" + else + echo -en " $red$ave$normal" + fi +done +echo + +running=0; sleeping=0 stopped=0; zombie=0 +for pid in /proc/[1-9]* +do + procs=$((procs+1)) + stat=`awk '{print $3}' $pid/stat` + case $stat in + R) running=$((running+1));; + S) sleeping=$((sleeping+1));; + T) stopped=$((stopped+1));; + Z) zombie=$((zombie+1));; + esac +done +echo -n "Process Count: " +echo -e "$white$procs$normal total $white$running$normal running $white$sleeping$normal sleeping $white$stopped$normal stopped $white$zombie$normal zombie" +echo diff --git a/Chapter_36/canned_messages/about_to_expire b/Chapter_36/canned_messages/about_to_expire new file mode 100644 index 0000000..b86c945 --- /dev/null +++ b/Chapter_36/canned_messages/about_to_expire @@ -0,0 +1,19 @@ +The $ENVIRONMENT UNIX account password for \\"$USERID\\" will expire in $REMAINING day\\(s\\) + +========================================================================= +++ ACTION NEEDS TO BE TAKEN OR YOUR ACCOUNT WILL BE LOCKED IN $REMAINING day\\(s\\)++ +========================================================================= + +$ENVIRONMENT UNIX passwords must be changed every $VALID_DAYS days. If the password is not changed within $REMAINING day\\(s\\), the account will be locked. + +Your account may continue to work after it has been locked if you only access it through r commands \\(rsh, rlogin etc\\). 90 days after the account is locked, it will be removed and you will need to apply for a new one if necessary. + +Instructions for changing passwords are located at: + + http://server.company.com/documentation/user_access_info.html + +If you are unable to change your password or need further assistance, please call the Help Desk. + +If you no longer need this account, please reply to this message and let us know so we can remove it. + +System Administration diff --git a/Chapter_36/canned_messages/about_to_expire.regular b/Chapter_36/canned_messages/about_to_expire.regular new file mode 100644 index 0000000..5e3484c --- /dev/null +++ b/Chapter_36/canned_messages/about_to_expire.regular @@ -0,0 +1,19 @@ +The $ENVIRONMENT UNIX account password for \\"$USERID\\" will expire in $REMAINING day\\(s\\) + +========================================================================= +++ ACTION NEEDS TO BE TAKEN OR YOUR ACCOUNT WILL BE LOCKED IN $REMAINING day\\(s\\)++ +========================================================================= + +$ENVIRONMENT UNIX passwords must be changed every $VALID_DAYS days. If the password is not changed within $REMAINING day\\(s\\), the account will be locked. + +Your account may continue to work after it has been locked if you only access it through r commands \\(rsh, rlogin etc\\). 90 days after the account is locked, it will be removed and you will need to apply for a new one if necessary. + +Instructions for changing passwords are located at: + + http://server.compayny.com/documentation/user_access_info.html + +If you are unable to change your password or need further assistance, please call the Help Desk. + +If you no longer need this account, please reply to this message and let us know so we can remove it. + +System Administration diff --git a/Chapter_36/canned_messages/about_to_expire.security b/Chapter_36/canned_messages/about_to_expire.security new file mode 100644 index 0000000..7439393 --- /dev/null +++ b/Chapter_36/canned_messages/about_to_expire.security @@ -0,0 +1,17 @@ +The $ENVIRONMENT UNIX account password for \\"$USERID\\" will expire in $REMAINING day\\(s\\) + +========================================================================= +++ ACTION NEEDS TO BE TAKEN OR YOUR ACCOUNT WILL BE LOCKED IN $REMAINING day\\(s\\)++ +========================================================================= + +If the password is not changed within $REMAINING day\\(s\\), the account will be locked. + +Instructions for changing passwords are located at: + + http://server.company.com/documentation/user_access_info.html + +If you are unable to change your password or need further assistance, please call the Help Desk. + +If you no longer need this account, please reply to this message and let us know so we can remove it. + +System Administration diff --git a/Chapter_36/canned_messages/account_locked b/Chapter_36/canned_messages/account_locked new file mode 100644 index 0000000..cc6ec71 --- /dev/null +++ b/Chapter_36/canned_messages/account_locked @@ -0,0 +1,13 @@ +The $ENVIRONMENT UNIX account \\"$USERID\\" has been locked. + +$ENVIRONMENT UNIX passwords must be changed every $VALID_DAYS days. Because the password for this account was not changed within the appropriate time period, the account has been locked. + +In order to have the account unlocked, you must E-Mail 'Accounts'. + +If your account remains locked for $VALID_DAYS days, it will be removed and you will need to request a new account if necessary. + +If this locking of your password is incorrect, call the Help Desk and request a password reset. + +If you no longer need this account, please reply to this message and let us know so we can remove it. + +System Administration diff --git a/Chapter_36/canned_messages/account_locked.regular b/Chapter_36/canned_messages/account_locked.regular new file mode 100644 index 0000000..cc6ec71 --- /dev/null +++ b/Chapter_36/canned_messages/account_locked.regular @@ -0,0 +1,13 @@ +The $ENVIRONMENT UNIX account \\"$USERID\\" has been locked. + +$ENVIRONMENT UNIX passwords must be changed every $VALID_DAYS days. Because the password for this account was not changed within the appropriate time period, the account has been locked. + +In order to have the account unlocked, you must E-Mail 'Accounts'. + +If your account remains locked for $VALID_DAYS days, it will be removed and you will need to request a new account if necessary. + +If this locking of your password is incorrect, call the Help Desk and request a password reset. + +If you no longer need this account, please reply to this message and let us know so we can remove it. + +System Administration diff --git a/Chapter_36/canned_messages/account_locked.security b/Chapter_36/canned_messages/account_locked.security new file mode 100644 index 0000000..f46c1a1 --- /dev/null +++ b/Chapter_36/canned_messages/account_locked.security @@ -0,0 +1,9 @@ +The $ENVIRONMENT UNIX account \\"$USERID\\" has been locked. + +Because the password for this account was not changed within the appropriate time period, the account has been locked. + +In order to have the account unlocked, you must E-Mail 'Accounts'. + +If you no longer need this account, please reply to this message and let us know so we can remove it. + +System Administration diff --git a/Chapter_36/pwaging b/Chapter_36/pwaging new file mode 100644 index 0000000..d2662a4 --- /dev/null +++ b/Chapter_36/pwaging @@ -0,0 +1,255 @@ +#!/bin/sh + +# +# Track the aging of unchanged passwords on a system. +# + +HOME=/usr/local/pwaging +# This is the number of days that the users password is valid before being +# locked. +VALID_DAYS=90 +# The environment. This is used to populate the notifications +ENVIRONMENT="Peterros House" +# Admin email +ADMIN_EMAIL=root +# If debug email is non-null, the shadow file will NOT be updated AND users +# will NOT be notified. User notifications will be sent to this debug +# address. +DEBUG_EMAIL= +# The shadow file to use. Wise to manually run on copies of the real +# passwd and shadow files. +shad=/usr/local/pass_aging/bin/shadow_copy +pswd=/usr/local/pass_aging/bin/passwd_copy +# The exclude file. A flat file of a list of usernames that should not be +# touched. +exclude="$HOME/config/exclude_list" + +ED=ed.script +max=$VALID_DAYS +notify=$(($max-14)) +OUTFILE=$HOME/aging +NOTEOUT=$HOME/notes +WARNOUT=$HOME/warnings +REPORT=$HOME/report + +ARCHIVE=$HOME/archive +BIN="$HOME/bin" + +if [ "`id -un`" != "root" ]; then + echo "This script must be run as root - exiting" >&2 + exit 1 +fi + +# +# Clean up the old report +# +for file in $OUTFILE $WARNOUT $NOTEOUT $REPORT +do + if [ -f $file ] + then + rm $file + fi +done + +# +# Figure out days since 1/1/1970 +# +seconds_since_epoch=$((`date +%s`)) +seconds_per_day=86400 +days_since_epoch=$(($seconds_since_epoch/$seconds_per_day)) + +# +# backup the $shad file for safety +# +backdate=`date +%m%d%y%H%M` +cp -p $shad $ARCHIVE/shadow.$backdate + +# +# cleanup the archive +# +find $ARCHIVE -mtime +7 -exec rm {} \; + + +for user in `cut -d: -f1 $pswd` +do + padding="" + user_length=`echo $user | awk '{print length}'` + padding_len=$((15-$user_length)) + counter=1 + while [ $counter -lt $padding_len ] + do + padding="${padding} " + counter=$(($counter+1)) + done + # + # Get some values from the user shadow entry + # + exp_days=`grep "^${user}:" $shad | cut -d: -f5` + pass_days=`grep "^${user}:" $shad | cut -d: -f3` + # Depending on whether the encrypted password is in the passwd or + # shadow file, use one of the following lines. + #pass_word=`grep "^${user}:" $pswd | cut -d: -f2` + pass_word=`grep "^${user}:" $shad | cut -d: -f2` + + if [ "$pass_word" = "*" ] + then + pass_word="\*" + fi + exempt=`grep "^${user}$" $exclude` + if [ "$pass_word" = "" ] + then + # + # Send warning of null password to outfile + # + echo "$user $padding WARN: $user has null password set, set password or lock account" >> $WARNOUT + fi + # + # See if the user is exempt from password expiring + # + if [ "$exempt" = "" ] + then + # + # Make sure the password has an expiration set. + # + if [ "$pass_days" != "" -a "$exp_days" != "" ] + then + # + # figure out how many days since the password has changed. + # + days_since_change=$(($days_since_epoch-$pass_days)) + + if [ $days_since_change -lt $notify ] + then + first_char=`echo $pass_word | cut -c1` + # + # See if the account is already locked + # + if [ "$first_char" = "*" ] + then + echo "$user $padding $days_since_change Already locked" >> $OUTFILE + else + # + # report user is ok to outfile + # + echo "$user $padding $days_since_change OK" >> $OUTFILE + fi + elif [ $days_since_change -ge $notify -a $days_since_change -le $max ] + then + # + # password will expire in $exp days + # + exp=$(($max-$days_since_change)) + + # + # report warning notification to the outfile + # + if [ "$DEBUG_EMAIL" != "" ] + then + echo "$user $padding $days_since_change Expires in $exp days ; Would have sent mail ; sent mail to $DEBUG_EMAIL" >> $OUTFILE + else + echo "$user $padding $days_since_change Expires in $exp days ; sending mail" >> $OUTFILE + fi + # + # notify user that password will expire in $exp days. Note: if the + # DEBUG_EMAIL is NOT null, notifications will be sent to the + # address specified. If it is null, notifications will be sent + # to the specified user. Note that the send_email script does the + # checking for DEBUG as well. + # + $BIN/send_email $user $days_since_change about_to_expire + else + # + # Set some variables + # + first_char=`echo $pass_word | cut -c1` + echo "User: $user Password: $pass_word The first character is $first_char" + the_date=`date +%y%m%d` + CLOSED="*CLOSED_${the_date}*" + # + # See if the account is already locked + # + if [ "$first_char" = "*" ] + then + echo "$user $padding $days_since_change Already locked" >> $OUTFILE + else + # + # Remove any previously existing ed script + # + if [ -f $HOME/$ED ] + then + rm $HOME/$ED + fi + + if [ "$DEBUG_EMAIL" != "" ] + then + echo "$user $padding $days_since_change Would have locked account ; sent mail to $DEBUG_EMAIL" >> $OUTFILE + else + # + # Write the status for this user to the outfile + # + echo "$user $padding $days_since_change Locking account *CLOSED_${the_date}*" >> $OUTFILE + # + # Make sure the encrypted password can be handled. a '.' and '/' + # are valid characters in that string so make sure they are + # replaced with '\.' and '\/' respectively. + # + pass_word=`echo $pass_word | sed -e s/\\\./\\\\\\\\./g` + pass_word=`echo $pass_word | sed -e s/\\\*/\\\\\\\\*/g` + pass_word=`echo $pass_word | sed -e s/\\\$/\\\\\\\\$/g` + pass_word=`echo $pass_word | sed -e s/\\\\\//\\\\\\\\\\\//g` + # + # Cut off the last 2 characters because of the $ sign. + # + pass_word=`echo $pass_word | sed -e 's/\(.*\)\(.\)\(.\)$/\1/'` + + + # + # Create the ed script that will replace the encrypted password + # + echo "/$user:$pass_word/s/$pass_word/$CLOSED" > $HOME/$ED + echo "w" >> $HOME/$ED + echo "q" >> $HOME/$ED + + # + # Run the ed script that will replace the encrypted password + # Change the shadow file to the passwd file if you don't have + # a real shadow file and the encrypted password is in the + # passwd file + # ed -s $pswd < $HOME/$ED > /dev/null + ed -s $shad < $HOME/$ED > /dev/null + + fi + # + # Send email account locked notification to the user + # + if [ "$DEBUG_EMAIL" = "" ] + then + $BIN/send_email $user $days_since_change account_locked + fi + fi + fi + else + # + # Report non-expiring account to the outfile + # + echo "$user $padding WARN: $user password not set to expire. Fix shadow entry" >> $WARNOUT + fi + else + # + # Report exempt account to the outfile + # + echo "$user $padding Note: $user is exempt from password expiring" >> $NOTEOUT + fi +done + +for file in $WARNOUT $OUTFILE $NOTEOUT +do + if [ -f $file ] + then + sort -rn +1 $file >> $REPORT + rm $file + fi +done + +cat $REPORT | mail -s "$ENVIRONMENT password aging report" $ADMIN_EMAIL +mv $REPORT $ARCHIVE/report.$backdate diff --git a/Chapter_36/send_email b/Chapter_36/send_email new file mode 100644 index 0000000..598e1e3 --- /dev/null +++ b/Chapter_36/send_email @@ -0,0 +1,49 @@ +#!/bin/ksh + +# This is the script that is called by the pwaging command which sends +# either about to be locked or accont locked notifications. +# +# If the DEBUG_EMAIL value in the config file is set, all mail will +# go to that account instead of notifying the actual user. +# +#usage: send_email username days_since_pwchange (about_to_expire|account_locked) +# +# rbpeters 07/11/03 Created. +# +HOME=/var/yp/bin/pwaging +. $HOME/config/pwaging.conf +messages=$HOME/canned_messages +MAIL=`which mailx` +BC=`which bc` + +#echo "Parameter count: $#" + +if [ $# -ne 3 ] +then + echo "usage $0 username days_since_pwchange (about_to_expire|account_locked)" +else + USERID=$1 + if [ "$DEBUG_EMAIL" != "" ] + then + RECIPIENT=$DEBUG_EMAIL + else + echo passwd is $pswd + pwdline=`grep "^$USERID" $pswd` + if [ "$pwdline" != "" ] + then + RECIPIENT=$USERID + fi + fi + if [ "$RECIPIENT" = "" ] + then + RECIPIENT=root + fi + days_since_pwchange=$2 + message=$3 + REMAINING=`echo $VALID_DAYS-$days_since_pwchange | $BC` + cat $messages/$message | while read duh + do + holder=`eval echo $duh` + echo $holder + done | $MAIL -s "$ENVIRONMENT UNIX Account Notification" $RECIPIENT +fi diff --git a/Chapter_37/nis_pwaging b/Chapter_37/nis_pwaging new file mode 100644 index 0000000..ddfb38e --- /dev/null +++ b/Chapter_37/nis_pwaging @@ -0,0 +1,136 @@ +#!/bin/ksh +#set -x + +# +# Maintain a pseudo shadow file for systems such as NIS that don't +# use one. This is so password aging can be performed on that type +# of environment. +# + +HOME=/usr/local/pass_aging +ARCHIVE=$HOME/archive +BIN="$HOME/bin" +# The environment. This is used to populate the notifications +ENVIRONMENT="Scripting" +# Admin email +ADMIN_EMAIL=sysadmin +# If debug is non-null, output will be displayed when run +DEBUG=duh +# The shadow file to use. +shad=/usr/local/pass_aging/bin/shadow_nis +pswd=/var/yp/src/passwd +#pswd=/var/yp/src/passwd +PERL=/usr/bin/perl +ED=ed.script + +if [ "`id | grep \(root\)`" = "" ]; then + echo "This script must be run as root - exiting" >&2 + exit 1 +fi + +if [ ! -f $HOME/config/nis_pwaging.conf ]; then + echo "Error: No config file found, exiting" +else + . $HOME/config/nis_pwaging.conf +fi + +# +# Figure out days since 1/1/1970 +# +seconds_since_epoch=`$PERL -e 'print time'` +seconds_per_day=$((60*60*24)) +days_since_epoch=$(($seconds_since_epoch/$seconds_per_day)) + +# +# If $shad does not exist, create the initial one. +# +if [ ! -f $shad ] +then + test "$DEBUG" != "" && echo DEBUG: $shad does not exist, creating + cat $pswd | awk -v days=$days_since_epoch -F: '{print $1 ":" $2 ":" days ":0:90:7:::"}' > $shad +fi +# +# backup the $shad file for safety +# +backdate=`date +%m%d%y%H%M` +test "$DEBUG" != "" && echo DEBUG: Backing up $shad to $ARCHIVE/nis_shadow.$backdate +cp -p $shad $ARCHIVE/nis_shadow.$backdate + +# +# cleanup the archive +# +find $ARCHIVE -mtime +7 -exec rm {} \; + +# +# Remove any previously existing ed script +# +if [ -f $HOME/bin/$ED ] +then + test "$DEBUG" != "" && echo DEBUG: Cleaning up old ed.script + rm $HOME/bin/$ED +fi + +for user in `cut -d: -f1 $pswd` +do + # + # Get some values from the user passwd/shadow entries + # + cur_pass_word=`grep "^${user}:" $pswd | cut -d: -f2` + user_exist=`grep "^${user}:" $shad | cut -d: -f1` + if [ "$user_exist" = "" ] + then + # Add any new users to the shadow file + echo "$user:$cur_pass_word:$days_since_epoch:0:90:7:::" >> $shad + test "$DEBUG" != "" && echo DEBUG: Missing $user, adding to $shad + fi + pass_days=`grep "^${user}:" $shad | cut -d: -f3` + old_pass_word=`grep "^${user}:" $shad | cut -d: -f2` + #test "$DEBUG" != "" && echo DEBUG: $user, $cur_pass_word, $old_pass_word, $pass_days + + if [ "$old_pass_word" != "$cur_pass_word" ] + then + test "$DEBUG" != "" && echo DEBUG: $user password has changed, updating $shad + # + # Change it in the shadow file and update the days since change field. + # + + # + # Make sure the encrypted passwords can be handled. a '.' , '/' and '$' + # are valid characters in that string so make sure they are + # replaced with '\.' and '\/' respectively. + # + old_pass_word=`echo $old_pass_word | sed -e s/\\\./\\\\\\\\./g` + old_pass_word=`echo $old_pass_word | sed -e s/\\\*/\\\\\\\\*/g` + old_pass_word=`echo $old_pass_word | sed -e s/\\\$/\\\\\\\\$/g` + old_pass_word=`echo $old_pass_word | sed -e s/\\\\\//\\\\\\\\\\\//g` + + cur_pass_word=`echo $cur_pass_word | sed -e s/\\\./\\\\\\\\./g` + cur_pass_word=`echo $cur_pass_word | sed -e s/\\\*/\\\\\\\\*/g` + cur_pass_word=`echo $cur_pass_word | sed -e s/\\\$/\\\\\\\\$/g` + cur_pass_word=`echo $cur_pass_word | sed -e s/\\\\\//\\\\\\\\\\\//g` + + # Whack the last 2 characters off of the encrypted passwords. Since + # there are valid $ characters, the end of line $-sign is then + # escaped above which we don't want. + + old_pass_word=`echo $old_pass_word | sed 's/\(.*\)\(.\)\(.\)$/\1/'` + cur_pass_word=`echo $cur_pass_word | sed 's/\(.*\)\(.\)\(.\)$/\1/'` + + # + # Create the ed script that will replace the encrypted password + ## This could probably be done with one file instead of one 'ed' for + ## each username + # + test "$DEBUG" != "" && echo DEBUG: Creating ed file to change $old_pass_word:$pass_days to $cur_pass_word:$days_since_epoch + echo "g/$user:$old_pass_word/s/$old_pass_word:$pass_days/$cur_pass_word:$days_since_epoch/g" >> $HOME/bin/$ED + else + test "$DEBUG" != "" && echo DEBUG: No changes for $user + continue + fi +done + +# Complete and process the file with the ed.script +echo "w" >> $HOME/bin/$ED +echo "q" >> $HOME/bin/$ED +test "$DEBUG" != "" && echo DEBUG: Running ed.script for $user on $shad +ed -s $shad < $HOME/bin/$ED > /dev/null diff --git a/Chapter_38/gold_build b/Chapter_38/gold_build new file mode 100644 index 0000000..a909057 --- /dev/null +++ b/Chapter_38/gold_build @@ -0,0 +1,242 @@ +#!/bin/sh + +# +# Build a mirrored copy of a system while accounting for possible +# drive hardware changes from the original. +# + +# How big is the disk and set up some variables +# sfdisk -l /dev/hda + +bytes_per_cyl=`sfdisk -l /dev/hda | grep Units | awk '{print $5}'` +#echo bytes_per_cyl $bytes_per_cyl + +cyl_count=`sfdisk -l /dev/hda | grep Disk | awk '{print $3}'` +#echo cyl_count $cyl_count + +heads=`sfdisk -l /dev/hda | grep Disk | awk '{print $5}'` +tracks_per_cyl=$heads +#echo tracks_per_cyl $tracks_per_cyl + +sectors_per_track=`sfdisk -l /dev/hda | grep Disk | awk '{print $7}'` +#echo sectors_per_track $sectors_per_track + +sectors_per_cyl=$(($tracks_per_cyl*$sectors_per_track)) +#echo sectors_per_cyl $sectors_per_cyl + +bytes_per_sector=$(($bytes_per_cyl/$sectors_per_cyl)) +#echo bytes_per_sector $bytes_per_sector + +usable_cyl=$(($cyl_count-4)) +disk_in_sectors=$(($sectors_per_cyl*$usable_cyl)) +disk_in_sectors=$(($disk_in_sectors-$sectors_per_track)) +#echo disk_in_sectors $disk_in_sectors + +mem_in_bytes=`cat /proc/meminfo | grep MemTotal: | awk '{print $2}'` +swap_in_bytes=$(($mem_in_bytes*2)) +#echo swap_in_bytes $mem_in_bytes + +# +# Setting up slice sizes in sectors for sfdisk input file. +# + +#echo disk in sectors $disk_in_sectors + +# +# sector_calc: function to calculate the correct number of sectors for +# a slice which land on cylinder boundaries. +# usage: sector_calc {slice size in bytes} +# + +sector_calc () { + +size=$1 + +slice_in_sectors=$(($size/$bytes_per_sector)) +slice_in_cyl=$(($slice_in_sectors/$sectors_per_cyl)) +slice_rem=$(($size%$bytes_per_sector)) +if [ $slice_rem -ne 0 ] +then + # + # Always rounding up to next even cylinder + # + slice_in_cyl=$(($slice_in_cyl+1)) + slice_in_sectors=$(($slice_in_cyl*$sectors_per_cyl)) + slice_in_sectors=$(($slice_in_sectors-$sectors_per_track)) +else + slice_in_sectors=$(($slice_in_cyl*$sectors_per_cyl)) + slice_in_sectors=$(($slice_in_sectors-$sectors_per_track)) +fi + +} + +# +# Get the sizes in sectors for each slice +# + +# Swap +sector_calc $swap_in_bytes +swap_in_sectors=$slice_in_sectors +#echo swap_in_sectors $swap_in_sectors +#echo swap in cylinders is `expr $swap_in_sectors \/ $sectors_per_cyl` + +# Root / +sector_calc 250000000 +root_in_sectors=$slice_in_sectors +#echo root_in_sectors $root_in_sectors +#echo root in cylinders is `expr $root_in_sectors \/ $sectors_per_cyl` + +# /boot +sector_calc 30000000 +boot_in_sectors=$slice_in_sectors +#echo boot_in_sectors $boot_in_sectors +#echo boot in cylinders is `expr $boot_in_sectors \/ $sectors_per_cyl` + +# /var +sector_calc 1000000000 +var_in_sectors=$slice_in_sectors +#echo var_in_sectors $var_in_sectors +#echo var in cylinders is `expr $var_in_sectors \/ $sectors_per_cyl` + +# /home +sector_calc 512000000 +home_in_sectors=$slice_in_sectors +#echo home_in_sectors $home_in_sectors +#echo home in cylinders is `expr $home_in_sectors \/ $sectors_per_cyl` + +# /tmp +sector_calc 512000000 +tmp_in_sectors=$slice_in_sectors +#echo tmp_in_sectors $tmp_in_sectors +#echo tmp in cylinders is `expr $tmp_in_sectors \/ $sectors_per_cyl` + +# +# Since /usr and /usr/local are evenly using up the rest of the disk, +# get their sector sizes. Rounding up in the function above isn't an issue +# because these slices are just taking what is left over. +# +# /usr +rest_of_disk_in_sectors=$(($disk_in_sectors-$swap_in_sectors-$boot_in_sectors-$root_in_sectors-$var_in_sectors-$home_in_sectors-$tmp_in_sectors)) +usr_in_sectors=$(($rest_of_disk_in_sectors/2/$sectors_per_cyl*$sectors_per_cyl)) +#echo usr_in_sectors $usr_in_sectors +#echo usr in cylinders is `expr $usr_in_sectors \/ $sectors_per_cyl` + +# /usr/local should be the same size as /usr +usrlocal_in_sectors=$usr_in_sectors +#echo usrlocal_in_sectors $usrlocal_in_sectors +#echo usrlocal in cylinders is `expr $usr_in_sectors \/ $sectors_per_cyl` + +# +# Set up start positions... +# + +# +# Define the partition table temporary file and write it out. This is based +# on reproducing the output from sfdisk -d /dev/hda. The start position is +# what gets kinda sticky because of adding the previous slice sizes. +# + +PARTTAB=/tmp/parts.out +double_sectors_per_track=$((2*$sectors_per_track)) + +cat > $PARTTAB < /dev/null 2>&1 + then + answer="${answer}alive" + else + answer="${answer}" + fi + done + if [ "$answer" != "" ] + then + echo network is working... + continue + else + + # Put an entry in the log file and switch the nics + logger -i -t nic_switch -f $LOG "Ping failed on $PINGLIST" + logger -i -t nic_switch -f $LOG "Possible nic or switch failure. Moving $IP from $PRIMARY to $SECONDARY" + # The plumb and unplumb work in Solaris and may need to be set for other + # OS's. Your mileage may vary. FreeBSD uses create and destroy. + #ifconfig $PRIMARY unplumb + #ifconfig $SECONDARY plumb + ifconfig $SECONDARY $IP netmask $NETMASK broadcast $BROADCAST + # This may or may not be necessary. Sometimes the new interface doesn't + # start in the up formation. + ifconfig $SECONDARY up + + echo "`date +%b\ %d\ %T` $ME nic_switch[$$]: Possible nic or switch failure. Moving $IP from $PRIMARY to $SECONDARY" | mail -s "Nic failover performed on $ME" $MAILLIST + + # Switch primary and secondary nodes. + place_holder=$PRIMARY + PRIMARY=$SECONDARY + SECONDARY=$place_holder + fi +done diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..e4ddafb --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,27 @@ +Freeware License, some rights reserved + +Copyright (c) 2009 Ron Peters + +Permission is hereby granted, free of charge, to anyone obtaining a copy +of this software and associated documentation files (the "Software"), +to work with the Software within the limits of freeware distribution and fair use. +This includes the rights to use, copy, and modify the Software for personal use. +Users are also allowed and encouraged to submit corrections and modifications +to the Software for the benefit of other users. + +It is not allowed to reuse, modify, or redistribute the Software for +commercial use in any way, or for a user’s educational materials such as books +or blog articles without prior permission from the copyright holder. + +The above copyright notice and this permission notice need to be included +in all copies or substantial portions of the software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..ce0e964 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +#Apress Source Code + +This repository accompanies [*Expert Shell Scripting*](http://www.apress.com/9781430218418) by Ron Peters (Apress, 2009). + +![Cover image](9781430218418.jpg) + +Download the files as a zip using the green button, or clone the repository to your machine using Git. + +##Releases + +Release v1.0 corresponds to the code in the published book, without corrections or updates. + +##Contributions + +See the file Contributing.md for more information on how you can contribute to this repository. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..24c1b4a --- /dev/null +++ b/README.txt @@ -0,0 +1,6 @@ +Expert Shell Scripting + +This contains the scripts that are used in the Expert Shell Scripting book. +Not all chapters in the book contain a single cohesive script but are +potentially simple commands or short code snippets. Only chapters with +a whole script or multiple scripts are included here. diff --git a/TOC.txt b/TOC.txt new file mode 100644 index 0000000..30a5117 --- /dev/null +++ b/TOC.txt @@ -0,0 +1,133 @@ +Chapter_03: +total 12 +-rwxrwxr-x 1 rbpeters 500 665 Oct 24 18:47 epoch_days +-rwxrwxr-x 1 rbpeters 500 1340 Oct 24 18:48 epoch_seconds +-rwxrwxr-x 1 rbpeters 500 928 Oct 24 18:49 time2notify + +Chapter_05: +total 12 +-rw-r--r-- 1 rbpeters users 1068 Oct 24 19:27 adm_opts +-rw-r--r-- 1 rbpeters users 35 Jan 27 22:19 myapp.sh +-rw-r--r-- 1 rbpeters users 2225 Oct 24 19:30 use_opts + +Chapter_07: +total 8 +-rwx--x--x 1 rbpeters 500 4880 Oct 17 19:22 logwatch + +Chapter_08: +total 12 +-rwxrwxr-x 1 rbpeters 500 1599 Oct 24 19:30 bash_ptree +-rwxrwxr-x 1 rbpeters users 1709 Oct 24 19:31 ksh_ptree +-rwxr-xr-x 1 rbpeters users 1570 Oct 24 19:31 sh_ptree + +Chapter_13: +total 4 +lrwxr-xr-x 1 rbpeters users 10 Apr 3 2005 boot_check -> root_check +-rwxr-xr-x 1 rbpeters users 507 Oct 24 19:32 root_check +lrwxr-xr-x 1 rbpeters users 10 Apr 3 2005 snap_check -> root_check + +Chapter_14: +total 8 +-rwxr-xr-x 1 rbpeters users 2443 Oct 28 22:48 bash_urlvalidator +-rwxr-xr-x 1 rbpeters users 2192 Oct 28 22:51 ksh_urlvalidator + +Chapter_17: +total 8 +-rwxr-xr-x 1 rbpeters users 1667 Oct 31 22:08 termserv_info +-rwxr-xr-x 1 rbpeters users 1098 Oct 31 22:56 xyp_connect + +Chapter_18: +total 16 +-rwxr-xr-x 1 rbpeters users 224 Oct 24 19:39 buildit +-rwxr-xr-x 1 rbpeters users 238 Oct 24 19:40 killit +-rwxr-xr-x 1 rbpeters users 222 Oct 24 19:40 readit +-rwxr-xr-x 1 rbpeters users 731 Oct 24 19:41 timeout + +Chapter_19: +total 4 +-rwxrwxr-x 1 rbpeters 500 1788 Nov 4 22:59 hot_keys + +Chapter_20: +total 4 +-rwxr-xr-x 1 rbpeters users 357 Oct 24 19:43 dircp + +Chapter_21: +total 12 +-rw-r--r-- 1 rbpeters users 510 Oct 24 19:44 profile +-rw-r--r-- 1 rbpeters users 715 Oct 24 19:45 root_profile +-rwxr-xr-x 1 rbpeters users 1205 Oct 24 19:45 throw_ssh_root + +Chapter_22: +total 8 +-rwx--x--x 1 rbpeters 500 2347 Oct 24 19:47 where.sh +-rwx--x--x 1 rbpeters 500 2014 Oct 24 19:48 window.sh + +Chapter_23: +total 8 +-rwxr-xr-x 1 rbpeters users 1086 Oct 24 19:48 mime_attach +-rwxr-xr-x 1 rbpeters users 602 Oct 24 19:48 uu_attach + +Chapter_27: +total 8 +-rwxr-xr-x 1 rbpeters users 481 Oct 24 19:52 reverse +-rwxr-xr-x 1 rbpeters users 506 Oct 24 19:51 reverse2 + +Chapter_29: +total 4 +-rwxr-xr-x 1 rbpeters users 320 May 25 2005 auto_ftp + +Chapter_30: +total 8 +-rw------- 1 rbpeters users 826 Jan 12 22:02 00.procmailrc +-rwx------ 1 rbpeters root 1783 Oct 24 19:56 getthisfile + +Chapter_31: +total 8 +-rwxrwxr-x 1 rbpeters 500 6317 Oct 24 19:56 kill_proc + +Chapter_32: +total 4 +-rwxr-xr-x 1 rbpeters users 646 Oct 24 19:57 mon_files + +Chapter_34: +total 4 +-rwxr-xr-x 1 rbpeters users 1050 Oct 24 19:57 rcs_vi + +Chapter_35: +total 4 +-rwxr-xr-x 1 rbpeters users 3696 Oct 24 19:58 sysreport + +Chapter_36: +total 16 +drwxr-xr-x 2 rbpeters users 4096 Jan 13 22:18 canned_messages +-rwxr-xr-x 1 rbpeters users 7403 Jan 22 21:55 pwaging +-rwxr-xr-x 1 rbpeters users 1187 Nov 18 22:58 send_email + +Chapter_36/canned_messages: +total 24 +-rw------- 1 rbpeters users 1072 Jan 13 22:15 about_to_expire +-rw------- 1 rbpeters users 1073 Jan 13 22:15 about_to_expire.regular +-rw------- 1 rbpeters users 768 Jan 13 22:16 about_to_expire.security +-rw------- 1 rbpeters users 670 Jan 13 22:17 account_locked +-rw------- 1 rbpeters users 670 Jan 13 22:17 account_locked.regular +-rw------- 1 rbpeters users 375 Jan 13 22:17 account_locked.security + +Chapter_37: +total 8 +-rwxrwxr-x 1 rbpeters users 4373 Oct 24 20:05 nis_pwaging + +Chapter_38: +total 8 +-rwxr-xr-x 1 rbpeters users 6757 Oct 24 20:06 gold_build + +Chapter_39: +total 8 +-rwxr-xr-x 1 rbpeters users 7289 Feb 25 2005 system_snapshot + +Chapter_41: +total 4 +-rwxr-xr-x 1 rbpeters users 752 Oct 24 20:08 core_finder + +Chapter_42: +total 4 +-rwxr-xr-x 1 rbpeters users 3283 Feb 21 2005 nic_switch diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..f6005ad --- /dev/null +++ b/contributing.md @@ -0,0 +1,14 @@ +# Contributing to Apress Source Code + +Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. + +## How to Contribute + +1. Make sure you have a GitHub account. +2. Fork the repository for the relevant book. +3. Create a new branch on which to make your change, e.g. +`git checkout -b my_code_contribution` +4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. +5. Submit a pull request. + +Thank you for your contribution! \ No newline at end of file