Wednesday, July 29, 2009

bak2disc-tpb

#!/bin/bash
################################################################################
#
# bak2disc - backup to disc volumes while maintaining original data structure
# Copyright (C) 2005-2006 dorphell <dorphell [AT] gmail [DOT] com>
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
################################################################################

#--------------------------------------------------------------------
# PUBLIC CONSTANTS [Default Parameters]
#--------------------------------------------------------------------

## Dir used for meta-data (a few MBs of ASCII files) ##
TMP_DIR="${TEMP:-/tmp/bak2disc}"
mkdir -p $TMP_DIR

## Disc type (valid types: CD74, CD80, DVD5, DVD9, DLT7, LTO2, LTO3) ##
MEDIA_TYPE="DVD5"

## Space allocated for index metadata (MiB)##
INDEX_SIZE=4

## mkisofs options used by growisofs ##
MKISOFS_OPTS=" -r -J "

## Limit for the volume-filling search in the element array ##
SEARCH_RANGE=150

Label="Volume"


#--------------------------------------------------------------------
# PRIVATE CONSTANTS [Do not Modify]
#--------------------------------------------------------------------

VERSION="0.7-tpb"
PROG_NAME="bak2disc"
GB=1073741824         # 1024^3
MB=1048576            # 1024^2

## ANSI Color modifiers ##
C_BOLD="\e[1m"        # Bold
C_NORM="\e[m\e[0;39m" # Normal
C_BLUE="\e[1;34m"     # Blue
C_RED="\e[1;31m"      # Red

## Only use newlines for field separation ##
IFS=$'\n'

## Error Messages ##
ErrorMsgs=(
"00: No error"
"01: Unknown error"
"02: Program \'\$2\' not in \\\$PATH"
"03: Working directory \'\$2\' already exists"
"04: Permission denied or file/directory does not exist: \$2"
"05: File[s] too big, choose a higher-capacity disc or suppress oversized file[s]"
"06: Temporary directory \'\$2\' exists"
"07: Could not create temporary directory \'\$2\'"
"08: User reported failed burn for disc volume \$2, --resume to try again"
"09: Option \'\$2\' requires an argument"
"10: Invalid option: \$2"
"11: Previous session metadata not found. Incorrect --temp parameter?"
"12: No options given, what do you want to do?"
"13: No \'--include\' files/directories defined"
"14: Disc \$2 index larger than allocated space, increase --index size"
"15: Please provide a single valid operating mode"
"16: Unknown disc type \'\$2\'. Valid types: cD74, CD80, DVD5, DVD9, DLT7, LTO2, LTO3"
"17: SIGINT caught"
"18: Invalid --speed argument, value must be an integer"
"19: mkisofs is really genisoimage which will not work")

#--------------------------------------------------------------------
# FUNCTIONS
#--------------------------------------------------------------------

Help() {
cat << EOF
$PROG_NAME v$VERSION (c) 2006 dorphell
Usage: $PROG_NAME <mode> [options]
Backup to disc volumes while maintaining original data structure

    Informative flags:
    -h, --help      Print help screen
    -v, --version   Print version information

    Operating mode flags: (one required)
    -f, --fresh     Start a new backup session
    -n, --noburn    Generate backup volume information but don't burn to disc
    -r, --resume    Resume the burn phase of a previous backup session (relies
                    on original metadata)
    -w, --wipe      Clean up temporary directory and exit

    Accessibility flags:
    -j, --eject     Open drive tray for disc loading/unloading and skip the
                    verification prompt.
    -l, --exlarge   Automatically exclude files too large for backup media
    -c, --nocolor   Turn off all text color/highlighting

    Configuration options: (every option requires an argument)
    -i, --include   Colon-separated file/directory paths to include (multiple
                    definitions and bash globbing wildcards are also vallid)
    -e, --exclude   File/directory paths to exclude (same syntax as --include)
    -t, --temp      Temporary directory [default: $TMP_DIR]
    -o, --mkisofs   Burning options passed onto mkisofs/growisofs
                    [default: $MKISOFS_OPTS]
    -m, --mtype     Disc type (CD74, CD80, DVD5, DVD9, DLT7, LTO2, LTO3) [default: $MEDIA_TYPE]
    -x, --index     Space allocated for index metadata out of disc capacity
                    [default: $INDEX_SIZE]
    -g, --range     Number of files/directories to poll when filling slack
                    space on volumes [default: $SEARCH_RANGE]
    -s, --speed     Speed factor of the writing process (integer value)
                    [default: Maximum supported by the drive]
    --label         Label the .iso and volume name

    Examples:
    $PROG_NAME --fresh --include "/etc:/mnt/HD*:/opt" -d /dev/hdc --mtype DVD5
    $PROG_NAME -n -i /usr -i /var -e /var/log --temp /tmp --range 10 -m DVD5
    $PROG_NAME --resume --temp /var/tmp

    GNUtar:
    gtar cf <tape> -T $TMP_DIR/Volume_1

EOF
}

Version() {
   echo "$PROG_NAME $VERSION"
}

## ErrorExit "int error_code" "string reference_name" ##
ErrorExit() {
   echo -ne "$C_BLUE" >&2
   echo -ne ">> "
   echo -ne "$C_RED" >&2
   eval echo -n "ERROR ${ErrorMsgs[$1]}"
   echo -ne "$C_NORM" >&2
   echo
   exit $1
}

## PrintBold "string message" ##
PrintBold() {
   echo -ne "$C_BOLD" >&2
   echo -ne "$1"
   echo -ne "$C_NORM" >&2
}

## PrintStatus "string message" ##
PrintStatus() {
   echo -ne "$C_BLUE" >&2
   echo -ne ">>"
   echo -ne "$C_NORM$C_BOLD " >&2
   echo -ne "$1"
   echo -ne "$C_NORM" >&2
   echo
}

## YesNo "string question" "boolean default" [y/n] ##
YesNo() {
   OPTIONS="[y/n]"
   [ "$2" == "y" ] && OPTIONS="[Y/n] "
   [ "$2" == "n" ] && OPTIONS="[y/N] "

   PrintBold "$1 $OPTIONS"
   IFS=$' \t\n' read ANSWER
   [ ! "$ANSWER" ] && [ "$2" ] && ANSWER="$2"
   while [ "$ANSWER" != 'y' ] && [ "$ANSWER" != 'n' ] && [ "$ANSWER" != 'Y' ] && [ "$ANSWER" != 'N' ]; do
      PrintBold "Invalid response, please answer 'y' or 'n': "
      IFS=$' \t\n' read ANSWER
   done
   [[ "$ANSWER" == 'y' || "$ANSWER" == 'Y' ]] && return 0 || return 1
}

## CheckPath "string program_name" ##
CheckPath() {
   while [ "$1" ]; do
      ! type "$1" &>/dev/null && ErrorExit 2 "$1"
      shift
   done
}

## ParseArgs "string args" (e.g. "$@") ##
ParseArgs() {
   while [ "$1" ]; do
   ## Informative flags ##
      case "$1" in
         -h|--help)
            Help;                  exit ;;
         -v|--version)
            Version;               exit ;;
   ## Operating modes flags ##
         -f|--fresh)
            FRESH=1;              shift ;;
         -r|--resume)
            RESUME=1;             shift ;;
         -n|--noburn)
            NOBURN=1;             shift ;;
         -w|--wipe)
            WIPE=1;               shift ;;
   ## Accessibility flags ##
         -j|--eject)
            CheckPath 'eject'
            EJECT=1;              shift ;;
         -l|--exlarge)
            AUTO_EXCLUDE=1;       shift ;;
         -c|--nocolor)
            NOCOLOR=1;            shift ;;
   ## Configuration options ##
         -i|--include)
            [ ! "$2" ] && ErrorExit 9 "$1"
          INCLUDE="$INCLUDE:$2"; shift 2;;
         -e|--exclude)
            [ ! "$2" ] && ErrorExit 9 "$1"
          EXCLUDE="$EXCLUDE:$2"; shift 2;;
         -o|--mkisofs)
            [ ! "$2" ] && ErrorExit 9 "$1"
            MKISOFS_OPTS="$2";   shift 2;;
         -m|--mtype)
            [ ! "$2" ] && ErrorExit 9 "$1"
            MEDIA_TYPE="$2";     shift 2;;
         -x|--index)
            [ ! "$2" ] && ErrorExit 9 "$1"
            INDEX_SIZE="$2";     shift 2;;
         -t|--temp)
            [ ! "$2" ] && ErrorExit 9 "$1"
            TMP_DIR="$2";        shift 2;;
         -g|--range)
            [ ! "$2" ] && ErrorExit 9 "$1"
            SEARCH_RANGE="$2";   shift 2;;
         -s|--speed)
            [ ! "$2" ] && ErrorExit 9 "$1"
      [ "${2##*[^0-9]*}" ] || ErrorExit 18
            SPEED="-speed=$2";   shift 2;;
         --label)
             [ ! "$2" ] && ErrorExit 9 "$1"
             Label="$2"; shift 2;;
         *)
            Help;      ErrorExit 10 "$1";;
      esac
   done
}


## SetMediaSize "string preset" (e.g. SetMediaSize "dvd9") ##
SetMediaSize() {
   MEDIA_TYPE="$(echo $1| tr a-z A-Z)"
   case "$MEDIA_TYPE" in
      CD74) MEDIA_SIZE=650  ;;
      CD80) MEDIA_SIZE=703  ;;
      DVD5) MEDIA_SIZE=4482 ;;
      DVD9) MEDIA_SIZE=8144 ;;
      DLT7) MEDIA_SIZE=30000 ;;
      LTO2) MEDIA_SIZE=190000 ;;
      LTO2c) MEDIA_SIZE=323000 ;;
      LTO3) MEDIA_SIZE=390000;;
      LTO3c) MEDIA_SIZE=663000;;
         *) Help;
            ErrorExit 16 "$1" ;;
   esac
   let "MEDIA_SIZE-=INDEX_SIZE"
}


## ValidatePath "string path" "string output_variable" ##
## If path is readable, return its proper pathname; else error + exit ##
ValidatePath() {
   [ ! -r "$1" ] && CleanUp && ErrorExit 4 "$1"
   [ "$1" == '/' ] && return
   eval "$2=\"$(echo "$1"| sed 's|/\+|/|g;s|/$||')\""
}

## GetList "string du_output_syntax" (e.g. "1234 <TAB> /path/to/dir") ##
## Returns a list of files/dirs that are less than media size ##
GetList() {
   ## Set Size and Name ##
   VARS=(${1//$'\t'/$'\n'})
   Size=${VARS[0]}
   Name=${VARS[1]}
   if [ $Size -gt $((MEDIA_SIZE*MB)) ] && [ -d "$Name" ]; then
      for x in $($DU -ba --max-depth=1 "$Name"| gawk -F '\t' '$2 != "'$Name'" {print}'); do
         GetList "$x"
      done
   else
      printf "%s\t%s\n" "$Name" "$Size"
   fi
}

## Burn disc volumes defined in TMP_DIR ##
BurnVolumes() {
   NumOfDiscs=$(head -n 1 "$TMP_DIR/summary" 2>/dev/null| gawk '{print $8}')
   MEDIA_TYPE=$(head -n 1 "$TMP_DIR/summary" 2>/dev/null| gawk '{print $9}')

   ## Check if metadata exists ##
   [ ! $NumOfDiscs ] && ErrorExit 11

   ## If --resume, reprint distribution summary ##
   if [ $RESUME ]; then
      PrintStatus "Resuming burn session..."
      while read line; do
         vol=$(echo $line| gawk '$1 == "Disc" {printf "%i\n", $2}')
         [ ${line:0:1} == " " ] && echo "$line"        && continue
         [ ${line:0:1} == "I" ] && PrintStatus "$line" && continue
         [ -z $vol ] && PrintBold "$line\n" && continue
         [ -e "$TMP_DIR/Volume_$vol" ] && PrintBold "$line\n" || echo "$line [Done]"
      done < "$TMP_DIR/summary"
      echo
   fi


## Make ISO files
#   for (( DISC_NUM=1; DISC_NUM<=$NumOfDiscs; DISC_NUM++ )); do
   DISC_NUM=1
   while [ $DISC_NUM -le $NumOfDiscs ]; do
      ## Volume already burned ##
      [ ! -e "$TMP_DIR/Volume_$DISC_NUM" ] && continue


      ## Burn the volume ##
      ALL_MKISOFS_OPTS="$MKISOFS_OPTS -quiet -V ${Label}-${DISC_NUM}_of_$NumOfDiscs -graft-points -exclude-list $EXC_LIST -path-list $TMP_DIR/Volume_$DISC_NUM  -o ${Label}_$DISC_NUM.iso"

      IFS=$' \n';
      echo "mkisofs $ALL_MKISOFS_OPTS"
      mkisofs $ALL_MKISOFS_OPTS
      IFS=$'\n'

      ## Volume successfully burned, delete volume metadata ##
      rm "$TMP_DIR/Volume_$DISC_NUM" "$TMP_DIR/volume_${DISC_NUM}_of_${NumOfDiscs}"
      DISC_NUM=$(expr $DISC_NUM + 1)
   done

   ## Backup complete, wipe out metadata ##
   PrintStatus "All $NumOfDiscs backup volumes burned."
   CleanUp
}

## CleanUp (removes "$TMP_DIR" entirely) ##
CleanUp() {
   PrintStatus "Cleaning up temporary data"
   rm -rf "$TMP_DIR"
}

Terminate() {
   echo
   ErrorExit 17
}

#--------------------------------------------------------------------
# SCRIPT START
#--------------------------------------------------------------------

## Capture ^C exit code ##
trap Terminate SIGINT

## Check if utilities are in $PATH ##
# Solaris /bin/du won't work.  Look for GNU du named gdu
[ -f $(which gdu) ] && DU=$(which gdu) || DU=$(which du)
[ $(uname -s) == "SunOS" -a $(basename $DU) != "gdu" ] && ErrorExit 2 "gdu"

CheckPath 'gawk' 'sed' 'printf' 'sort' "$DU" 'stat' 'comm' 'mkisofs'

mkisofs --version | grep -s genisoimage > /dev/null
[ $? -eq 0 ] && echo "mkisofs is really genisoimage which will not work"

## Parse command-line arguments ##
[ -z "$1" ] && Help && ErrorExit 12
ParseArgs "$@"

ValidatePath "$TMP_DIR" "TMP_DIR"

TMP_DIR="$TMP_DIR/$PROG_NAME-$USER"
FILE_LIST="$TMP_DIR/include"
EXC_LIST="$TMP_DIR/exclude"

SetMediaSize "$MEDIA_TYPE"

[ $NOCOLOR ] && unset C_BOLD C_NORM C_BLUE C_RED

## No operating modes defined, error ##
[ $((FRESH+NOBURN+RESUME+WIPE)) -ne 1 ] && Help && ErrorExit 15

## If --resume specified, just burn using preexisting metadata ##
[ $RESUME ] && BurnVolumes && exit

## Delete any garbage/leftovers and start fresh ##
[ $WIPE ] && CleanUp && exit

## Check for leftovers, if not clean, ask what to do ##
if [ -e "$TMP_DIR" ]; then
   ! YesNo "Leftover metadata detected; DELETE and start fresh?" "n" && PrintBold "Nothing dnoe... exiting\n" && exit
   CleanUp
fi

## Check/Ask for required variables ##
[ ! "$INCLUDE" ] && Help && ErrorExit 13

PrintBold "$C_RED-------------------------------------------------------------------$C_NORM\n"
PrintBold " Do not modify included files until they have been written to disc\n"
PrintBold " Excessive growth may cause volume to overflow disc capacity\n"
PrintBold "$C_RED-------------------------------------------------------------------$C_NORM\n"

## Start a fresh backup now ##
mkdir -p "$TMP_DIR" || ErrorExit 7 "$TMP_DIR"

## Convert path lists to arrays ##
INCLUDE=(${INCLUDE//:/$'\n'})
EXCLUDE=(${EXCLUDE//:/$'\n'})

## Validate all dir/file paths ##
x=0
while [ $x -lt ${#INCLUDE[@]} ]; do ValidatePath "${INCLUDE[$x]}" "INCLUDE[$x]"; x=$(expr $x + 1); done
x=0
while [ $x -lt ${#EXCLUDE[@]} ]; do ValidatePath "${EXCLUDE[$x]}" "EXCLUDE[$x]"; x=$(expr $x + 1); done

## Remove all dupes ##
for x in "${INCLUDE[@]}"; do INCLUDE=($(echo "${INCLUDE[*]}"| sed "\|^$x/|d"| sort -u)); done
for x in "${EXCLUDE[@]}"; do EXCLUDE=($(echo "${EXCLUDE[*]}"| sed "\|^$x/|d"| sort -u)); done

## Generate element filelist from INCLUDE paths ##
for x in "${INCLUDE[@]}"; do
   GetList "$($DU -bs "$x")"
done| sort > "$FILE_LIST"

## Remove excluded items from filelist ##
if [ "${EXCLUDE[*]}" ]; then
   for item in $($DU -bs "${EXCLUDE[@]}"); do
      item=(${item//$'\t'/$'\n'})
      Size=${item[0]}
      Name=${item[1]}

      ## *Exact* match ##
      Exists=$(gawk -F '\t' '$1 == "'$Name'" || $1 ~ "'$Name'/" {print $1}' "$FILE_LIST")

      if [ "$Exists" ]; then
         for x in $Exists; do sed "\|^$x\t.*|d" -i "$FILE_LIST"; done
      else
         Parent=""
         while [ ! "$Parent" ] && [ "$Name" ]; do
            Name="${Name%/*}"
            Parent=$(gawk -F '\t' '$1 == "'$Name'" {print}' "$FILE_LIST")
         done
         Parent=(${Parent//$'\t'/$'\n'})
         PName=${Parent[0]}
         PSize=${Parent[1]}
         NewSize=$((PSize-Size))
         sed "s|\(^$PName\)\t\(.*$\)|\1\t$NewSize|" -i "$FILE_LIST"
      fi
   done
fi

## Initialize element array ##
ELEMENTS=($(cat "$FILE_LIST"))

## Fill content elements into disc volumes ##
Current=0; Count=0; CountRange=0; declare -i Size; LastElement=${#ELEMENTS[@]}

while [ "${ELEMENTS[*]}" ]; do
   line=(${ELEMENTS[$Count]//$'\t'/$'\n'})
   Name="${line[0]}" # File name
   Size="${line[1]}" # File size

   if [ $Size -gt $((MEDIA_SIZE*MB)) ]; then                      # file bigger than disc capacity
      AEXCLUDE[${#AEXCLUDE[@]}]="$Name"                           # add file to auto-exclude list
#      unset ELEMENTS[$Count] Name
      unset ELEMENTS[$Count]
      unset Name
   fi

   if [ ! $Name ] && [ $Count -ne $CountRange ]; then             # skip null elements
      :
   elif [ $CountRange -ne 0 ] && [ $Count -eq $CountRange ]; then # end of search range, volume filled
      CountRange=0
      let "Count=KeepCount-1"                                     # go to 1st element that didn't fit
      let "Current++"
#      echo "$Current"
#      echo "$((VolSizes[$Current]+Size))  $((MEDIA_SIZE*MB))"

   elif [ $((VolSizes[$Current]+Size)) -lt $((MEDIA_SIZE*MB)) ]; then
      VolSizes[$Current]=$((${VolSizes[$Current]}+$Size))
      VolNames[$Current]="${VolNames[$Current]}$Name\n"
      unset ELEMENTS[$Count]
   else                                                           # element too big for current vol
      if [ $CountRange -eq 0 ]; then                              # remember element position
         KeepCount=$Count
         let "CountRange=Count+SEARCH_RANGE"
         if [ $CountRange -gt $LastElement ]; then                # don't set range outside the array!
            CountRange=$LastElement
         fi
      fi
   fi
   let "Count++"
done

## Ask if big files should be auto-excluded ##
if [ "$AEXCLUDE" ]; then
   if [ ! $AUTO_EXCLUDE ]; then
      for x in "${AEXCLUDE[@]}"; do echo "| $x"; done
      YesNo "+ The preceding file[s] will not fit on a $MEDIA_TYPE disc, exclude and continue?" "y" || ErrorExit 5
      echo
   fi
   EXCLUDE=("${EXCLUDE[@]}" "${AEXCLUDE[@]}")
fi

## Account for excluded files in distribution summary ##
find "${EXCLUDE[@]}" ! -type d 2>/dev/null| sort > "$EXC_LIST"

## Generate index and filelists ##
x=0;
while [ $x -lt ${#VolNames[@]} ]; do
   BurnName="Volume_$((x+1))"                      # e.g. Volume_1
   IndexName="volume_$((x+1))_of_${#VolNames[@]}"  # e.g. volume_1_of_16

   ## Generate mkisofs graft-points formated filelist ##
   echo -e "${VolNames[$x]}"| gawk -F '\n' '$1 {printf "data%s=%s\n", $1, $1}' > "$TMP_DIR/$BurnName"

   ## Generate volume reference index ##
   VolFiles[$x]=$(comm -23 <(find $(echo -e "${VolNames[$x]}") ! -type d 2>/dev/null| sort) "$EXC_LIST")
   echo "${VolFiles[$x]}" > "$TMP_DIR/$IndexName"

   ## Generate master catalog ##
   echo "### $IndexName ###" >> "$TMP_DIR/catalog.idx"
   echo -e "${VolFiles[$x]}\n"| sed 's|^/|'$((x+1))': /|' >> "$TMP_DIR/catalog.idx"

   ## Append reference index and master catalog to mkisofs input filelist ##
   #echo "$IndexName=$TMP_DIR/$IndexName" >> "$TMP_DIR/$BurnName"
   echo "catalog.idx=$TMP_DIR/catalog.idx" >> "$TMP_DIR/$BurnName"
   x=$(expr $x + 1 )
done

## Generate mkisofs exclude list file ##
echo "${EXCLUDE[*]}" > "$EXC_LIST"

TotalSize=$(echo "${VolSizes[*]}"| gawk '{sum+=$1} END {printf "%.2f", (sum/'$GB') }')
Index_Size=$(stat -c %s "$TMP_DIR/catalog.idx" 2>/dev/null)
TotalFiles=$(echo "${VolFiles[*]}"| grep -c "[^\n]")
NumOfDiscs=${#VolNames[@]}

## Overall backup summary ##
SUM_TOTALS=$(printf "Summary: %s files (%s GiB) divided into %s %s disc volumes" "$TotalFiles" "$TotalSize" "$NumOfDiscs" "$MEDIA_TYPE")
SUM_INC=$(for x in "${INCLUDE[@]}"; do echo "   $x"; done)
SUM_EXC=$(for x in "${EXCLUDE[@]}"; do echo "   $x"; done)

PrintBold "$SUM_TOTALS\n"; echo -e "$SUM_TOTALS" > "$TMP_DIR/summary"
                PrintStatus "Items Included:" && echo "$SUM_INC" && echo -e "Items Included:\n$SUM_INC" >> "$TMP_DIR/summary"
[ $EXCLUDE ] && PrintStatus "Items Excluded:" && echo "$SUM_EXC" && echo -e "Items Excluded:\n$SUM_EXC" >> "$TMP_DIR/summary"

## Disc volume distribution summary ##
x=0
while [ $x -lt $NumOfDiscs ]; do
   DISC_NUM=$((x+1))
   DISC_FILES=$(echo "${VolFiles[$x]}"| grep -c "[^\n]")
   VIDX_SIZE=$(stat -c %s "$TMP_DIR/volume_${DISC_NUM}_of_${NumOfDiscs}")
   DISC_SIZE=$(echo "${VolSizes[$x]}" $Index_Size $VIDX_SIZE $MB| gawk '{printf "%f", ($1+$2+$3)/$4 }')
   DISC_FREE=$(echo $MEDIA_SIZE $INDEX_SIZE $DISC_SIZE| gawk '{printf "%i", $1+$2-$3}')
   PRINT_DIST="$PRINT_DIST$(printf "Disc %3d: %5d files -> %8.2f MiB, %4i MiB free" "$DISC_NUM" "$DISC_FILES" "$DISC_SIZE" "$DISC_FREE")\n"
   [ $DISC_FREE -lt 1 ] && ErrorExit 14 $DISC_NUM
   x=$(expr $x + 1 )
done; PrintBold $PRINT_DIST

## Save distribution summary ##
echo -ne $PRINT_DIST >> "$TMP_DIR/summary"

## If --noburn specified, exit now ##
[ $NOBURN ] && PrintStatus "Backup metadata generated." && exit

## Ask whether to burn now or leave metadata for later ##
YesNo "\nBegin burn sequence now?" "y" && BurnVolumes

#--------------------------------------------------------------------
# SCRIPT END
#--------------------------------------------------------------------

Archiving a tree to DVD with bak2disc

bak2disc by dorphell is a script to create DVDs of a directory tree, preserving the structure. It will create an index file on the DVD so you can find which DVD the files are on.

I modified the script to create .iso files. You can also feed the file lists created to tar. So I modified the script to create lists contining X GB of data. I feed that to tar and can tar files to a DLT, SDLT, LTO, etc tape.

I sent an email and didn't get a reply. I'll try to get a version up somewhere.

Wednesday, June 11, 2008

Micro Irrigation

Article showing typical layout

HOWTO install a system
From Colorado State

RainBird automation
Tutorials
Irrigo systems - nice picture
Drip Works - end user vendor
Toro Micro Irrigation

Wednesday, June 4, 2008

Gardening

Just a few quick sites

Getting rid of weeds from http://www.thisgardenisillegal.com

Composting

http://myfolia.com/ is a garden tracking journal

According to http://www.garden.org, We are Zone 6a. And they have a nice plant zone finder.

Oh My!! GardenMon

Friday, February 8, 2008

Unsolicited phone calls


From Slashdot:


Here is an effective (though laborious) way to deal with that.



1. Register on National Do-Not-Call list.

2. Wait 3 month beginning period.

3. Get caller ID.

4. Wait for another call.

5. Be pleasant to the person, if you can order something cheap, say $10, do it.

6. Get their address and phone number as you place the order.

7. Photograph the Caller ID display as evidence.

8. Take good notes including date, time, person talked to, company name, as more evidence.

9. Copy the bill you receive for $10 as conclusive evidence of marketing intent.

10. Go to your county courthouse, lodge a small claim for $500 for a telemarketing violation.

11. Send them proper notice they are being sued.

12. Since they are often out of state, they won't show and you get default judgment.

13. If they do show, you have proof of listing, notice, call, and call purpose.

14. For bonus dollars, ($500 per item) look into whether they have,
train to, practice and publish upon demand the required company calling
policies.

15. Profit!!!



Powered by ScribeFire.

Sunday, February 3, 2008

Podcasting with an M-Audio Microtrack

Think of the MT (Microtrack) as you would a cassette recorder. Don't worry about listening. That's for later on the computer. The MT is to capture the sound. Much you'd use a camera to take lots of pictures of an event. You'll select relevant photos for your presentation, possibly editing and cleaning them up. You might change the order of the photos to better fit your presentation.


Make sure the battery is charged up.

Use the electret microphone on the wire, not the T mic. This lets you put the microphone near the sound.

On one side of the MT (microtrack) is a L/M/H switch. Put it on M for Microphone.
Put the microphone in its jack.

Press record to start recording. Do a test recording to check sound levels.

On the screen you'll see 2 bars, labeled L and R, for Left and Right.
The levels are the sensitivity of the sound. You want to adjust the level or sensitivity of the microphone so you get all the sound but not so sensitive that the sound is distorted.

Too low a level & the mic will not pickup (hear) any sound. Too high a level and there will be too much static and distortion. You might be able to hear someone's heartbeat ;-)

You want to adjust the levels so they are near the right side, but not hitting the end. Read your script while doing this until the levels are adjusted.

Hit record again to stop the recording. The MT will display "Writing" as it saves the recording as an MP3 file.

I'd suggest keeping a list of what you record:
  1. Sound check
  2. Script take #1
  3. Script take #2
  4. recording of speech to students
The 1st MP3 file on the MT is 1), etc...

Now that you've recorded, bring the MT over to the computer. Plug the USB cable into the MT and the computer.

The MT will appear as a disk called "MICRODRIVE" with all your recordings named file001.mp3, file002.mp3, etc. There is one for each time you pressed rec to start & then rec to end. Your log will help you figure out which is which.

They might not start number 001, but they will be numbered sequentially.

Create a folder in your My Documents folder and copy the files from the MICRODRIVE into it. Do not erase them from the MICRODRIVE yet. These are your originals. Some people make a CD of them so they have a read only copy.

Now you have your sound files to edit. MT supplies Audacity which is a good choice. You can copy it from the CD or download it off the internet.

I've never used Audacity, but basically it lets you edit your sound files. You can filter it to change the sound if your original recording wasn't optimal. Mostly, you'll be cutting out dead air and splicing snippets into place. You might find splicing the 1st half of take #2 with the 2nd half of take #3 to be the best choice. The better your original, the less to do latter.