#!/bin/ksh # # ident "@(#)flar.sh 1.24 00/10/09 SMI" # # Copyright (c) 2000-2001 by Sun Microsystems, Inc. # All rights reserved. # ###################################################### # # # flar -- Utility Functions for Flash Archives # # # ###################################################### TEXTDOMAIN=SUNW_INSTALL_FLASH export TEXTDOMAIN # list of required utilities. If you add any to this # list, be sure to also add it to the $UTIL_LIST variable below! AWK="/usr/bin/awk" RM="/usr/bin/rm" CPIO="/usr/bin/cpio" COMPRESS="/usr/bin/compress" UNCOMPRESS="/usr/bin/uncompress" CAT="/usr/bin/cat" ZCAT="/usr/bin/zcat" FIND="/usr/bin/find" TOUCH="/usr/bin/touch" MT="/usr/bin/mt" DD="/usr/bin/dd" MKDIR="/usr/bin/mkdir" UTIL_LIST="$AWK $RM $CPIO $COMPRESS $UNCOMPRESS $CAT $ZCAT $FIND $TOUCH $MT $DD $MKDIR" ############################################################## # Function find_utils makes sure required utilies exist. ############################################################## find_utils_or_exit () { for i in $UTIL_LIST ; do if [ ! -x $i ] ; then print_error_and_exit "$(gettext "Required utility %s is not available.")" $i fi done } ############################################################## # Function print_error_and_exit prints a formatted message # to stderr prefixed with ERROR: and new-lined, and exits 1. ############################################################## print_error_and_exit () { typeset format=$1 shift typeset values=$* printf "$(gettext 'ERROR:') ${format}\n" $values >&2 exit 1 } ############################################################## # Function print_message prints a formatted message to stderr ############################################################## print_message () { typeset format=$1 shift typeset values=$* printf "${format}\n" $values >&2 } ############################################## # Function print_usage_and_exit does just that ############################################## print_usage_and_exit () { # Remember to update the usage in flarcreate if you update this one typeset myname=`basename $0` print_message "$(gettext "Usage:")" if [[ $subcommand_selected -eq 0 || $do_create -eq 1 ]] ; then print_message "$myname $(gettext 'create -n name [-R root] [-H] [-S] [-c] [-t [-p posn] [-b blocksize]]')" print_message " $(gettext '[-i date] [-u section [-d path ]] [-U key=value] [-m master]')" print_message " $(gettext '[-f [ list_file | - ] [-F]]')" print_message " $(gettext '[-a author] [-e descr | -E descr_file] [-T type] [-x exclude]')" print_message " $(gettext 'archive')" fi if [[ $subcommand_selected -eq 0 ]] ; then print_message "" fi if [[ $subcommand_selected -eq 0 || $do_combine -eq 1 ]] ; then print_message "$myname $(gettext 'combine [-d dir] [-u section] [-t [-p posn] [-b blocksize]] archive')" fi if [[ $subcommand_selected -eq 0 ]] ; then print_message "" fi if [[ $subcommand_selected -eq 0 || $do_split -eq 1 ]] ; then print_message "$myname $(gettext 'split [-d dir] [-u section] [-f] [-S sect]')" print_message " $(gettext '[-t [-p posn] [-b blocksize]] archive')" fi if [[ $subcommand_selected -eq 0 ]] ; then print_message "" fi if [[ $subcommand_selected -eq 0 || $do_info -eq 1 ]] ; then print_message "$myname $(gettext 'info [-l] [-k keyword] [-t [-p posn] [-b blocksize]] archive')" fi exit 1 } ############################################################## # Function print_error_and_usage prints a formatted message to stderr # with ERROR: and calls print_usage_and_exit ############################################################## print_error_and_usage () { typeset format=$1 shift typeset values=$* printf "$(gettext 'ERROR:') ${format}\n" $values >&2 print_usage_and_exit } ############################################################## # Functions dial(), start_dial(), stop_dial(), and cleanup() # implement a spinner of |, /. -, and \. dial() runs in the # background. Use of traps is criticalcal to avoid spinning after # user has Control-C'd. ############################################################## dial() { typeset state=0 trap - EXIT INT # Set trap to ignore while [[ 1 -eq 1 ]] ; do case $state in 0) echo "|\b\c" # top-bottom state=1 ;; 1) echo "/\b\c" # upper right, lower left state=2 ;; 2) echo "-\b\c" # left-right state=3 ;; 3) echo "\\" "\b\b\c" # upper left, lower right state=0 ;; esac sleep 1 done } start_dial() { [[ $QUIET = $YES ]] && return dial & # Start in background DIALPID=$! # Get pid, for killing trap "cleanup 1" EXIT INT # Set trap } stop_dial() { [[ $QUIET = $YES ]] && return trap "" EXIT INT kill $DIALPID >/dev/null 2>&1 DIALPID= echo " \b\c" } cleanup() { typeset exitcode=$1 if [[ ! -z "$DIALPID" ]] ; then kill $DIALPID fi exit $exitcode } ################################################### # Function remove_from_list removes an element from # a list. If the element was not in the list, # the original list (minus any surrounding whitespace) # is returned on stdout. # # $1 = space-separated list # $2 = element to remove ################################################### remove_from_list() { typeset newlist for i in $1 ; do if [[ $i != $2 ]] ; then newlist="$newlist $i" fi done newlist=${newlist# } newlist=${newlist% } [ -n "$newlist" ] && { echo "$newlist" } } ################################################### # Function in_list examines list for existance # of an element # # $1 = space-separated list # $2 = element to look for # returns zero if found, non-zero if not ################################################### in_list() { for i in $1 ; do if [[ $i = $2 ]] ; then return 0 fi done return 1 } ################################################### # Function file_read_or_stop checks for readability ################################################### file_read_or_stop() { typeset input_file=$1 if [[ -r $input_file ]]; then return 0 else print_error_and_exit "$(gettext "File %s is not readable.")" $input_file fi } ################################################### # Function file_write_or_stop checks for writability ################################################### file_write_or_stop () { typeset output_file=$1 [[ -e $output_file ]] && ${RM} -rf $output_file ${TOUCH} $output_file if [[ -w $output_file ]]; then ${RM} -rf $output_file return 0 else print_error_and_exit "$(gettext "File %s could not be created for writing.")" $output_file fi } ################################################################## # Function get_compression sees what, if any, compression # Prints answer, so run under command execution ################################################################## get_compression () { typeset ident=$1 typeset line=$(grep "files_compressed_method" $ident) print ${line##*=} } ################################################### # Function gen_archive does -c processing # Puts all information to standard output for caller ################################################### gen_archive () { typeset result typeset command ${CAT} $use_directory/$cookie [[ $? -ne 0 ]] && return 1 [[ $DEBUG -ge 1 ]] && print_message "Wrote cookie." # remove cookie and archive (if in there) from list of sections section_list=`remove_from_list "$section_list" $cookie` section_list=`remove_from_list "$section_list" $archive` # Put each identification section for section in $section_list do print "section_begin="$section ${CAT} $use_directory/$section print "section_end="$section done # Put archive contents print "section_begin="$archive if [[ -d $use_directory/$archive ]]; then cd $use_directory/$archive if [[ $(get_compression ../$identification) = "compress" ]]; then ${FIND} . -print | ${CPIO} -oc 2>/dev/null | ${COMPRESS} -c else ${FIND} . -print | ${CPIO} -oc 2>/dev/null fi result=$? cd $current_dir else ${DD} <$use_directory/$archive 2>/dev/null result=$? fi [[ $result -ne 0 ]] && return 1 [[ $DEBUG -ge 1 ]] && print_message "Wrote archive." return 0 } ################################################### # Function gen_split does -s processing # Receives archive as standard input from caller ################################################### gen_split () { typeset old_ident= typeset result typeset done typeset section_done typeset key typeset val typeset process_archive=$NO # Read and copy cookie input_line=`line` if [[ $? -ne 0 ]]; then print_error_and_exit "$(gettext "Did not succeed in reading input file.")" fi if [[ ${input_line%%-[0-9]*.[0-9]*} != "FlAsH-aRcHiVe" ]] then print_error_and_exit "$(gettext "File %s is not an archive file.")" $archive_file fi # Validate cookie in_list "$section_list" $cookie && { print $input_line >$use_directory/$cookie [[ $DEBUG -ge 1 ]] && print_message "Extracted %s." \"$cookie\" section_list=`remove_from_list "$section_list" $cookie` } # remove archive from list, since it's assumed we'll work on it # later and we don't want to think of it as an unknown section if in_list "$section_list" $archive ; then section_list=`remove_from_list "$section_list" $archive` process_archive=$YES # force ident section to be extracted, so that we know # what the compression method is for extracting the # archive later on section_list="$identification $section_list" fi done=$NO while [ $done != $YES ] do # Read until past section_begin success=0 input_line=`line` key=${input_line%%=*} val=${input_line##*=} # we should always be at the start of a section here if [ "$key" != "section_begin" ] ; then print_error_and_exit "$(gettext "Invalid data between section")" fi section_name=$val # if we're at the archive, don't process any more sections. # if the section list isn't empty, warn about ignored sections if [[ "$section_name" = "$archive" ]] ; then if [ -n "$section_list" -a "$section_list" != $archive ] ; then print_message "$(gettext "WARNING: ignoring non-existant sections: %s")" $section_list fi done=$YES continue fi # skip past this section if not in the section list, # if we are doing custom lists if [ $custom_list == $YES ] && ! in_list "$section_list" $section_name ; then # Copy lines until detecting section_end success=0 while input_line=`line` do if [[ $input_line = "section_end=$section_name" ]]; then success=1 break fi # if we read all the way to the archive, something bad happened if [[ $input_line = "section_begin=$archive" ]] ; then print_error_and_exit "$(gettext "section %s has no end")" $section_name fi done if [[ $success = 0 ]] ; then # if we got here, we read to the end of the file before # seeing the end of the section we were reading; this is bad print_error_and_exit "$(gettext "EOF before end of section %s")" $section_name else # else we finished with the section. Go back and start # on the next section. continue fi else # we are about to process this section. Remove it # from the list first. section_list=`remove_from_list "$section_list" $section_name` fi section_done=$NO # read entire section, dumping its contents to disk, until the end of # the section while [ $section_done != $YES ] ; do # the following awk scripts reads lines from stdin until: # a) the end of the current section (success, exit level 0), # b) the beginning of the archive section is reached (error, exit level 1), # c) the end of the file is reached, error, exit level 2 ${AWK} " BEGIN { status = 2 } \ /^section_begin=$archive\$/ { status = 1; exit status } ; \ /^section_end=$section_name\$/ { status = 0; exit status } ; \ { print } ; \ END { exit status } \ " > $use_directory/$section_name if [ $? != 0 ] ; then # if we read all the way to the archive, or fell off the end of # the file, something bad happened print_error_and_exit "$(gettext "section %s has no end")" $section_name # never reached fi # if we get here, we successfully read to the end of the current # section, so let's fall out of the loop section_done=$YES done # print out something if we just extracted the ident section if [[ $section_name = $identification ]] ; then if [[ $DEBUG -ge 1 ]] ; then print_message "Extracted identification section" fi fi done # Copy remaining archive file to "archive", if it's in the list # to process if [[ $process_archive = $YES ]] ; then if [[ $arch_cpio = $YES ]]; then # Copy files as cpio output to $archive *directory* ${MKDIR} $use_directory/$archive if [[ $? -ne 0 ]]; then print_error_and_exit "$(gettext "Could not make directory %s.")" $use_directory/$archive fi cd $use_directory/$archive if [[ $(get_compression ../$identification) = "compress" ]]; then ${ZCAT} | ${CPIO} -icd 2>/dev/null else ${CPIO} -icd 2>/dev/null fi result=$? cd $current_dir else # Copy files as what they are to $archive *file* ${DD} >$use_directory/$archive 2>/dev/null result=$? fi [[ $result -ne 0 ]] && return 1 [[ $DEBUG -ge 1 ]] && print_message "Extracted %s." $archive fi return 0 } ################################################### # Prints a list of the files in the archive # coming in through stdin. ################################################### print_list () { typeset compression=$NO # Read ident success=0 in_line=`line` if [[ $? -ne 0 ]]; then print_error_and_exit "$(gettext "Could not read cookie from %s.")" $archive_file fi if [[ ${in_line%%-[0-9]*.[0-9]*} != "FlAsH-aRcHiVe" ]] then print_error_and_exit "$(gettext "File %s is not an archive file.")" $archive_file fi while input_line=`line` do if [[ $input_line = "section_begin=identification" ]]; then success=1 break fi done if [[ $success -eq 0 ]]; then print_error_and_exit "$(gettext "EOF on %s before finding ident section.")" $archive_file fi success=0 while input_line=`line` do if [[ $input_line = "section_end=identification" ]]; then success=1 break elif [[ $input_line = "files_compressed_method=compress" ]]; then compression=$YES fi done if [[ $success -eq 0 ]]; then print_error_and_exit "$(gettext "EOF on %s before finding end of ident section.")" $archive_file fi success=0 while input_line=`line` do if [[ $input_line = "section_begin=archive" ]]; then success=1 break fi done if [[ $success -eq 0 ]]; then print_error_and_exit "$(gettext "EOF on %s before finding archive section")" $archive_file fi success=0 pipeline_cmd="${CPIO} -ict" if [[ $compression = $YES ]] ; then pipeline_cmd="${UNCOMPRESS} -c | $pipeline_cmd" fi eval $pipeline_cmd 2> /dev/null if [ $? = 0 ] ; then success=1; fi if [[ $success -eq 0 ]]; then print_error_and_exit "$(gettext "EOF on %s before finding end of archive section")" $archive_file fi } ################################################### # prints indentification from stdin # from its caller. Also, validates the cookie. ################################################### print_info () { typeset in_line= typeset temp= in_line=`line` if [[ $? -ne 0 ]]; then print_error_and_exit "$(gettext "Could not read cookie from %s. Are you using a large enough block size?")" $archive_file fi if [[ ${in_line%%-[0-9]*.[0-9]*} != "FlAsH-aRcHiVe" ]] then print_error_and_exit "$(gettext "File %s is not an archive file.")" $archive_file else # Version information extracted for possible future use temp=${in_line#FlAsH-aRcHiVe-} major_version=${temp%.[0-9]*} minor_version=${temp#[0-9]*.} fi in_line=`line` if [[ $in_line != "section_begin=identification" ]]; then print_error_and_exit "$(gettext "File %s does not contain identification section.")" $archive_file fi while [ 1 ] ; do in_line=`line` if [[ $in_line = "section_end=identification" ]]; then # Finished; how to exit? if [[ ! -z $keyword_value ]]; then # Didn't find specified value, if we got here exit 2 else exit 0 fi else # If -k option, see if we have found it if [[ ! -z $keyword_value ]]; then if [[ ${in_line%%=*} = $keyword_value ]]; then print $in_line exit 0 fi else # Not -k, so print each line print $in_line fi fi done } ################################################### # gen_info processes -i, and expects standard input # from its caller. Also, validates the cookie. ################################################### gen_info () { if [ "$info_list" = $YES ] ; then print_list else print_info fi } ######################################################### # Execution starts here. # Miscellaneous intializations and beginning-of-job steps ######################################################### NO="No" YES="Yes" current_dir=$(pwd) # make sure required binaries exist find_utils_or_exit if [[ $# -lt 1 ]]; then print_usage_and_exit fi #################################################### # Initialize defaults and parse the invoking command #################################################### cookie="cookie" identification="identification" archive="archive" subcommand_selected=0 do_combine=0 do_split=0 do_info=0 do_create=0 gen_which= custom_list=$NO keyword_value= info_list=$NO arch_cpio=$NO master_section_list="$cookie $identification $archive" section_list="$master_section_list" use_directory=$(pwd) tape_position= tape_usage=$NO tape_blocksize= tape_block_default="64k" DEBUG=0 QUIET=$NO # Look for a leading keyword case "$1" in combine|-c) do_combine=1 ;; create) do_create=1 ;; info|-i) do_info=1 ;; split|-s) do_split=1 ;; *) print_usage_and_exit ;; esac shift subcommand_selected=1 # If they specified create, switch over to flarcreate. if [[ $do_create -eq 1 ]] ; then flarcreate=/usr/sbin/flarcreate if [[ -x $flarcreate ]] ; then exec $flarcreate "$@" else print_error_and_exit \ "$(gettext "Unable to find flarcreate as $flarcreate")" fi fi # Process the arguments while getopts ":b:d:fk:lp:qS:tu:v" opt; do case $opt in b ) tape_blocksize=$OPTARG ;; d ) use_directory=$OPTARG ;; f ) arch_cpio=$YES ;; k ) keyword_value=$OPTARG ;; l ) info_list=$YES ;; p ) tape_position=$OPTARG ;; q ) QUIET=$YES ;; # PRIVATE t ) tape_usage=$YES ;; u ) section_list="$section_list $OPTARG" ; custom_list=$YES ;; S ) section_list="$OPTARG" ; custom_list=$YES ;; v ) DEBUG=1 ;; # PRIVATE \? ) print_error_and_usage "$(gettext "Option -%s is invalid.")" $OPTARG ;; \: ) print_error_and_usage "$(gettext "Option -%s has no value.")" $OPTARG ;; * ) print_usage_and_exit ;; esac done shift $(($OPTIND - 1)) # Isolate outfile parameter - it should be the only argument left if [[ $# -gt 1 ]] ; then print_usage_and_exit elif [[ $# -eq 0 ]] ; then print_error_and_usage "$(gettext "Archive file name must be supplied.")" fi archive_file=$1 ############################################### # Make format, dependency, and existence checks ############################################### # Checking common to all options if [[ -z $archive_file ]]; then if [[ $tape_usage = $NO ]]; then print_error_and_usage "$(gettext "Path name for archive must be provided.")" else print_error_and_usage "$(gettext "Tape drive name for archive must be provided.")" fi fi if [[ $tape_usage = $NO ]]; then if [[ $archive_file = ${archive_file#/} ]]; then archive_file=$(pwd)/$archive_file fi fi if [[ ! -z $tape_position ]]; then if [[ $tape_usage = $NO ]]; then print_error_and_usage \ "$(gettext "Option -p is invalid in the absence of option -t.")" else # $tape_usage = $YES if [[ $tape_position != +([0-9]*) ]]; then print_error_and_usage \ "$(gettext "Tape position %s must be a number.")" $tape_position fi fi fi if [[ ! -z $tape_blocksize && $tape_usage = $NO ]]; then print_error_and_usage \ "$(gettext "Option -b is invalid in the absence of option -t.")" fi if [[ $tape_usage = $YES ]]; then tape_blocksize=${tape_blocksize:-$tape_block_default} if [[ $tape_blocksize != +([0-9])?(@(k|b|w)) ]]; then print_error_and_usage \ "$(gettext "Tape blocksize %s must be number[k|b|w].")" $tape_blocksize fi fi # Checking for -c option if [[ $do_combine -eq 1 ]]; then if [[ ! -d $use_directory ]]; then print_error_and_exit "$(gettext "Directory %s is not accessible.")" $use_directory fi if [[ $arch_cpio = $YES ]]; then print_error_and_usage "$(gettext "Option -f is not relevant to -c option.")" fi if [[ ! -z $keyword_value ]]; then print_error_and_usage "$(gettext "Option -k is not relevant to -c option.")" fi file_read_or_stop $use_directory/$cookie for section in $section_list do file_read_or_stop $use_directory/$section done file_read_or_stop $use_directory/$archive if [[ $tape_usage = $NO ]]; then ${RM} -f $archive_file ${TOUCH} $archive_file if [[ ! -w $archive_file ]]; then print_error_and_exit "$(gettext "File %s is not writable.")" $archive_file fi fi # Checking for -s option elif [[ $do_split -eq 1 ]]; then gen_which="gen_split" if [[ $arch_cpio = $YES ]] ; then ! in_list "$section_list" $archive && { print_error_and_usage \ - "$(gettext "Option -f is not relevant when archive is not produced.")" } fi if [[ ! -z $keyword_value ]]; then print_error_and_usage "$(gettext "Option -k is not relevant to -s option.")" fi if [[ ! -d $use_directory ]]; then print_error_and_exit \ "$(gettext "Directory %s is not accessible.")" $use_directory fi file_write_or_stop $use_directory/$cookie for section in $section_list do file_write_or_stop $use_directory/$section done file_write_or_stop $use_directory/$archive if [[ $tape_usage = $NO && ! -r $archive_file ]]; then print_error_and_exit "$(gettext "File %s is not readable.")" $archive_file fi # Checking for -i option elif [[ $do_info -eq 1 ]]; then gen_which="gen_info" if [ "$custom_list" = $YES -o $arch_cpio = $YES -o \ $use_directory != $(pwd) ]; then print_error_and_usage \ "$(gettext "Options -d, -f, -S, and -u are not relevant to option -i.")" fi if [[ $tape_usage = $NO && ! -r $archive_file ]]; then print_error_and_exit "$(gettext "File %s is not readable.")" $archive_file fi fi [[ $DEBUG -ge 1 ]] && print_message "Checks completed." ###################################### # Position magnetic tape, if requested ###################################### if [[ $tape_usage = $YES && ! -z $tape_position ]]; then print_message "$(gettext "Positioning tape drive...")" start_dial ${MT} -f $archive_file asf $tape_position if [[ $? -gt 0 ]]; then print_error_and_exit \ "$(gettext "Unable to move tape %s to position %d.")" $archive_file $tape_position fi stop_dial [[ $DEBUG -ge 1 ]] && print_message "Tape positioned." fi ######################################### # Perform the requested option ######################################### # Process -c if [[ $do_combine -eq 1 ]]; then if [[ $tape_usage = $NO ]]; then gen_archive >> $archive_file else gen_archive | ${DD} of=$archive_file obs=$tape_blocksize 2>/dev/null fi status=$? if [[ $status -ne 0 ]]; then print_error_and_exit "$(gettext "Unable to write archive file %s.")" $archive_file fi fi # Process -s and -i if [[ $do_split -eq 1 || $do_info -eq 1 ]]; then if [[ $tape_usage = $NO ]]; then $gen_which < $archive_file else ${DD} if=$archive_file ibs=$tape_blocksize 2>/dev/null | $gen_which fi status=$? if [[ $status -ne 0 ]]; then print_error_and_exit \ "$(gettext "Unable to process archive file %s.")" $archive_file fi fi ######################################### # Perform any EOJ processing, and exit ######################################### exit 0