#!/usr/bin/ksh # # (c)Copyright 1996 Hewlett-Packard Co., All Rights Reserved. # $Header: /ignite/cold_fusion/src/server_admin/archive_impact 10.11 1998/07/15 16:16:54 tee Exp $ # ## ## This program is run to calculate the per file system disk impacts for a ## given cpio or tar archive. It writes out its results as impacts lines ## which are intended to be placed in a Ignite-UX config file. ## ## Any archives which are produced with relative path names are assumed to ## be extracted relative to the "/" directory. ## ###################################################################### # # Function: init_globals # # Purpose: Initializes global variables # init_globals() { PATH=/bin:/sbin:/usr/bin:/usr/contrib/bin:/opt/ignite/lbin:/opt/ignite/bin:. FALSE=1 TRUE=0 LEVEL=1 CLEAN_EXIT=0 INTERRUPT_EXIT=1 BAD_OPTION_EXIT=2 OTHER_ERROR_EXIT=3 HELP_EXIT=4 BLOCK_SIZE=8192 FRAG_SIZE=1024 USAGE=" USAGE: $MY_NAME -t|-c [-g|-z] [-l level] [-b blocksize] [-f fragsize] archive_file_name -t The archive_file_name is a tar archive. Either this option or -c must be specified. -c The archive_file_name is a cpio archive. Either this option or -t must be specified. -g The archive_file_name has been compressed using gzip. -z The archive_file_name has been compressed using compress. -l level Determines what level in the file system hierarchy to build impacts statement for. For example, if level is 1, impacts statements might be generated for /usr and /var. If level is 2, impacts statements could be generated for /usr/local, /usr/bin, etc. It is not recommended to specify a level greater than 2 for performance reasons. The default is one. -b blocksize File system block size to assume. The default is $BLOCK_SIZE. -f fragsize File system fragment size to assume. The default is $FRAG_SIZE. " } ###################################################################### # # Function: cleanup_exit # # Purpose: to cleanup the system before exiting # # Inputs: $1 is the internal exit code # $2 is an optional error string # # Output: error or finish message # cleanup_exit() { ## # Type a few locals. ## trap 1 2 3 15 typeset exit_code typeset mesg ## # Set up the exit code. ## exit_code=$1 ## # Set up the correct message(s) to display. ## case $exit_code in $CLEAN_EXIT) ;; $INTERRUPT_EXIT) mesg="WARNING: Exiting due to user interrupt." ;; $BAD_OPTION_EXIT) mesg="ERROR: $2 Run $MY_NAME -? for help." ;; $OTHER_ERROR_EXIT) mesg="ERROR: $2" ;; $HELP_EXIT) print -u2 "$USAGE" ;; esac ## # Write the message if there is one. ## if [[ -n "$mesg" ]]; then print -u2 "$mesg" fi ## # Remove any temporary files we created. ## rm -f $INDEX_FILE ## # Exit with the appropriate status. ## exit $exit_code } ###################################################################### # # Function: numeric # # Purpose: determines if $1 is an integer # # Input: $1 # # Output: 0 if $1 is an integer, 1 if it is not numeric() { if (( $# != 1 )); then return 1 elif [[ "$1" = +([0-9]) ]]; then return 0 else return 1 fi } ###################################################################### # # Function: generate_sizes_for_cpio # # Purpose: do the impacts work for a cpio archive # # Input: None. # # Output: Writes impacts statements to stdout. No return code. generate_sizes_for_cpio() { cat_archive_to_stdout | cpio -ivt 2>/dev/null >$INDEX_FILE if (( $? != 0 )); then # check if archive was written with header information in ASCII cat_archive_to_stdout | cpio -icvt 2>/dev/null >$INDEX_FILE if (( $? != 0 )); then cleanup_exit $OTHER_ERROR_EXIT "The cpio command failed!" fi fi awk ' { ## # Pull out the basic fields that we will need. There is one # complicating factor here. The cpio -t command does not always # put a space (the separator) between the owner and the file size. # When we detect this case, we consider the size to be the # largest string of numbers at the end of the combined field. This # will still fail if a username ends in a number. ## if (NF==8) { mode=$1 name=$8 size=$3 } else { mode=$1 name=$7 pos=match($2,"[0-9]*$") size=substr($2,pos) } ## # Directories have a mode beginning with a 4. ## ct=index(mode,"4") if (ct==1) { type="d" } else { type="f" } ## # If there is no leading directory, add one. No need to handle # files which start with "./" since cpio cannot produce them. ## ct=index(name,"/") if (ct==1) { leading=" " } else { leading="/" } ## # Write out the record for this file/directory. ## printf "%s %s%s %s\n", type, leading, name, size } ' $INDEX_FILE | gen_impacts -l $LEVEL -b $BLOCK_SIZE -f $FRAG_SIZE | sort } ###################################################################### # # Function: generate_sizes_for_tar # # Purpose: do the impacts work for a tar archive # # Input: None. # # Output: Writes impacts statements to stdout. No return code. generate_sizes_for_tar() { cat_archive_to_stdout | tar tvf - >$INDEX_FILE 2>/dev/null if (( $? != 0 )); then cleanup_exit $OTHER_ERROR_EXIT "The tar command failed!" fi awk ' { ## # If the filename ends with a /, this is a directory. If # it is a directory, do not write out the final /. ## ct=match($8,"/$") if (ct!=0) { type="d" name=substr($8,1,ct-1) } else { type="f" name=$8 } ## # If there is no leading directory, add one. If the file starts # with "./" then remove the leading ".". ## ct=index(name,"/") if (ct!=1) { ct2=match(name,"^./") if (ct2!=1){ leading="/" } else { name=substr(name,2) leading=" " } } else { leading=" " } ## # Write out the record for this file/directory. ## printf "%s %s%s %s\n", type, leading, name, $3 } ' $INDEX_FILE | gen_impacts -l $LEVEL -b $BLOCK_SIZE -f $FRAG_SIZE | sort } ###################################################################### # # Function: cat_archive_to_stdout # # Purpose: zcat/gzcat/cat the archive to stdout so it can be piped # to tar/cpio # # Input: None. # # Output: An uncompressed archive image to stdout. cat_archive_to_stdout() { ## # Uncompress the file if it is compressed to stdout. ## if [[ $compress_mode = none ]]; then cat $archive_name elif [[ $compress_mode = compress ]]; then zcat $archive_name if (( $? != 0 )); then cleanup_exit $OTHER_ERROR_EXIT "The zcat command failed!" fi else $gzcat $archive_name # return value check removed because of bogus values in $? # legitimate error here will be caught down the pipe fi } ##################################################################### # Main Program ##################################################################### ## # Set up global variables and prepare for signals from user. ## MY_NAME="$(basename $0)" init_globals trap 'cleanup_exit $INTERRUPT_EXIT' 1 2 3 15 ## # Set up some temporary files. ## INDEX_FILE="/tmp/arc_imp_$$" rm -rf $INDEX_FILE ## # Initialize some locals. ## tar_mode=$FALSE cpio_mode=$FALSE compress_mode="none" gzcat="gzcat" ## # Parse the command line. ## while getopts :tcgzl:f:b: opt do case $opt in t) tar_mode=$TRUE ;; c) cpio_mode=$TRUE ;; g) compress_mode="gzip" ;; z) compress_mode="compress" ;; l) if numeric $OPTARG; then if (( OPTARG != 0 )); then LEVEL=$OPTARG else cleanup_exit $BAD_OPTION_EXIT "Cannot specify a zero level with -l." fi else cleanup_exit $BAD_OPTION_EXIT "Must specify a numeric level with -l." fi ;; b) BLOCK_SIZE=$OPTARG if numeric $BLOCK_SIZE; then if (( (BLOCK_SIZE % 1024) != 0 )); then cleanup_exit $BAD_OPTION_EXIT "The block size must be a multiple of 1024." fi else cleanup_exit $BAD_OPTION_EXIT "Must specify a numeric level with -b." fi ;; f) FRAG_SIZE=$OPTARG if numeric $FRAG_SIZE; then if (( (FRAG_SIZE % 1024) != 0 )); then cleanup_exit $BAD_OPTION_EXIT "The fragment size must be a multiple of 1024." fi else cleanup_exit $BAD_OPTION_EXIT "Must specify a numeric level with -f." fi ;; :) cleanup_exit $BAD_OPTION_EXIT "Must specify a parm with -$OPTARG." ;; \?) cleanup_exit $HELP_EXIT ;; esac done ## # Grab the archive name off of the end of the line. Make sure that # the file exists and is readable. ## shift $(($OPTIND -1)) archive_name="$1" if [[ -z "$archive_name" ]]; then cleanup_exit $BAD_OPTION_EXIT "Must specify an archive file to process." elif [[ ! -f "$archive_name" ]]; then cleanup_exit $OTHER_ERROR_EXIT "The archive $archive_name does not exist." elif [[ ! -r "$archive_name" ]]; then cleanup_exit $OTHER_ERROR_EXIT "The archive $archive_name is not readable." fi ## # check that compress command is available ## if [[ $compress_mode = compress ]]; then whence zcat >/dev/null if (( $? != 0 )); then cleanup_exit $OTHER_ERROR_EXIT "Could not find the zcat command!" fi elif [[ $compress_mode = gzip ]]; then whence gzcat >/dev/null if (( $? != 0 )); then gzcat="gunzip -c" whence gunzip >/dev/null if (( $? != 0 )); then cleanup_exit $OTHER_ERROR_EXIT "Could not find the gzcat or gunzip commands!" fi fi fi ## # Make sure that gen_impacts command is available ## whence gen_impacts >/dev/null if (( $? != 0 )); then cleanup_exit $OTHER_ERROR_EXIT "Could not find the gen_impacts command. The Ignite-UX.MGMT-TOOLS fileset must be installed on this system." fi ## # Make sure that one, and only one, of -c or -t was specified. If so, # call the correct routine. ## if (( tar_mode == TRUE && cpio_mode == TRUE )); then cleanup_exit $BAD_OPTION_EXIT "Cannot specify both -c and -t." elif (( tar_mode == TRUE )); then whence tar >/dev/null if (( $? != 0 )); then cleanup_exit $OTHER_ERROR_EXIT "Could not find the tar command!" fi generate_sizes_for_tar elif (( cpio_mode == TRUE )); then whence cpio >/dev/null if (( $? != 0 )); then cleanup_exit $OTHER_ERROR_EXIT "Could not find the cpio command!" fi generate_sizes_for_cpio else cleanup_exit $BAD_OPTION_EXIT "Must specify either -c or -t." fi ## # Exit with normal status. ## cleanup_exit $CLEAN_EXIT