tomb

the crypto undertaker
git clone git://parazyd.org/tomb.git
Log | Files | Refs | README | LICENSE

commit 21be9e204e7eead1f67ce6cf454e71591b311954
parent 58e2b26694b1ed73d04e61722295653cc94957cb
Author: Jaromil <jaromil@dyne.org>
Date:   Wed,  3 Aug 2011 23:58:14 -0700

Merge pull request #18 from boyska/only_optparsing

New CLI option parsing supports contextual options following commands.

ZSH functions are used to parse options. Further testing will follow.
Diffstat:
Msrc/tomb | 267+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/tomb-open | 10+++++-----
2 files changed, 179 insertions(+), 98 deletions(-)

diff --git a/src/tomb b/src/tomb @@ -24,20 +24,28 @@ VERSION=1.1 DATE=May/2011 TOMBEXEC=$0 TOMBOPENEXEC="tomb-open" +typeset -a OLDARGS +for arg in ${argv}; do OLDARGS+=($arg); done STEGHIDE=1 MOUNTOPTS="rw,noatime,nodev" +#declare global variables +QUIET=0 +DEBUG=0 +typeset -A global_opts +typeset -A opts + # PATH=/usr/bin:/usr/sbin:/bin:/sbin autoload colors; colors # standard output message routines # it's always useful to wrap them, in case we change behaviour later -notice() { if ! [ $QUIET ]; then print "$fg_bold[green][*]$fg_no_bold[white] $1" >&2; fi } -error() { if ! [ $QUIET ]; then print "$fg[red][!]$fg[white] $1" >&2; fi } -func() { if [ $DEBUG ]; then print "$fg[blue][D]$fg[white] $1" >&2; fi } +notice() { if [[ $QUIET == 0 ]]; then print "$fg_bold[green][*]$fg_no_bold[white] $1" >&2; fi } +error() { if [[ $QUIET == 0 ]]; then print "$fg[red][!]$fg[white] $1" >&2; fi } +func() { if [[ $DEBUG == 1 ]]; then print "$fg[blue][D]$fg[white] $1" >&2; fi } act() { - if ! [ $QUIET ]; then + if [[ $QUIET == 0 ]]; then if [ "$1" = "-n" ]; then print -n "$fg_bold[white] . $fg_no_bold[white] $2" >&2; else @@ -156,7 +164,7 @@ exec_as_user() { # escalate privileges check_priv() { if [ $UID != 0 ]; then - func "Using sudo for root execution of 'tomb ${(f)ARGS}'" + func "Using sudo for root execution of 'tomb ${(f)OLDARGS}'" # check if sudo has a timestamp active sudok=false sudo -n ${TOMBEXEC} 2> /dev/null @@ -165,12 +173,12 @@ check_priv() { OPTION ttyname=$TTY OPTION lc-ctype=$LANG SETTITLE Super user privileges required -SETDESC Sudo execution of Tomb ${ARGS[@]} +SETDESC Sudo execution of Tomb ${OLDARGS[@]} SETPROMPT Insert your USER password: GETPIN EOF fi - sudo "${TOMBEXEC}" ${(s: :)ARGS} + sudo "${TOMBEXEC}" "${(@)OLDARGS}" exit $? fi # are we root already return 0 @@ -312,8 +320,8 @@ EOF create_tomb() { if ! [ ${CMD2} ]; then - error "no tomb name specified for creation" - return 1 + error "no tomb name specified for creation" + return 1 fi tombfile=`basename ${CMD2}` @@ -321,6 +329,7 @@ create_tomb() { # make sure the file has a .tomb extension tombname=${tombfile%%\.*} tombfile=${tombname}.tomb + tombsize=$opts[-s] if [ -e ${tombdir}/${tombfile} ]; then error "tomb exists already. I'm not digging here:" @@ -336,17 +345,11 @@ create_tomb() { notice "Creating a new tomb in ${tombdir}/${tombfile}" - if [ -z $SIZE ]; then - if [ $CMD3 ]; then - tombsize=${CMD3} - else - act "No size specified, summoning the Tomb Undertaker to guide us in the creation." - "$TOMBOPENEXEC" & - wait $! - return 0 - fi - else - tombsize=${SIZE} + if [ -z $tombsize ]; then + act "No size specified, summoning the Tomb Undertaker to guide us in the creation." + "$TOMBOPENEXEC" & + wait $! + return 0 fi tombsize_4k=`expr $tombsize \* 1024 / 4` @@ -480,38 +483,45 @@ create_tomb() { mount_tomb() { notice "Commanded to open tomb $CMD2" get_arg_tomb $CMD2 + local tombkey + if option_is_set -k ; then + tombkey=`option_value -k` + else + tombkey="${PARAM[1]}.key" + fi + echo the key used is $tombkey if [ $? != 0 ]; then - error "operation aborted." - return 1 + error "operation aborted." + return 1 fi if ! [ $CMD3 ]; then - tombmount=/media/${tombfile} - act "mountpoint not specified, using default: $tombmount" + tombmount=/media/${tombfile} + act "mountpoint not specified, using default: $tombmount" elif ! [ -x $CMD3 ]; then - error "mountpoint $CMD3 doesn't exist, operation aborted." - if [ -n "$usbkey_mount" ]; then - umount $usbkey_mount - rmdir $usbkey_mount - unset usbkey_mount - fi - return 1 + error "mountpoint $CMD3 doesn't exist, operation aborted." + if [ -n "$usbkey_mount" ]; then + umount $usbkey_mount + rmdir $usbkey_mount + unset usbkey_mount + fi + return 1 else - tombmount=$CMD3 + tombmount=$CMD3 fi # check if its already open mount -l | grep "${tombname}.tomb.*\[$tombname\]$" 2>&1 > /dev/null if [ $? = 0 ]; then - error "$tombname is already mounted on $tombmount" - act "tomb list - show all tombs currently open" - if [ -n "$usbkey_mount" ]; then - umount $usbkey_mount - rmdir $usbkey_mount - unset usbkey_mount - fi - error "operation aborted." - return 1 + error "$tombname is already mounted on $tombmount" + act "tomb list - show all tombs currently open" + if [ -n "$usbkey_mount" ]; then + umount $usbkey_mount + rmdir $usbkey_mount + unset usbkey_mount + fi + error "operation aborted." + return 1 fi notice "mounting $tombfile on mountpoint $tombmount" @@ -522,18 +532,18 @@ mount_tomb() { nstloop=`losetup -f` if [ $? = 255 ]; then - error "too many tomb opened. Please close any of them to open another tomb" - exit 1 + error "too many tomb opened. Please close any of them to open another tomb" + exit 1 fi losetup -f ${tombdir}/${tombfile} act "check for a valid LUKS encrypted device" cryptsetup isLuks ${nstloop} if [ $? != 0 ]; then - # is it a LUKS encrypted nest? see cryptsetup(1) - error "$tombfile is not a valid Luks encrypted storage file" - $norm || rmdir $tombmount 2>/dev/null - return 1 + # is it a LUKS encrypted nest? see cryptsetup(1) + error "$tombfile is not a valid Luks encrypted storage file" + $norm || rmdir $tombmount 2>/dev/null + return 1 fi # save date of mount in minutes since 1970 @@ -562,10 +572,10 @@ mount_tomb() { done if ! [ -r /dev/mapper/${mapper} ]; then - error "failure mounting the encrypted file" - losetup -d ${nstloop} - $norm || rmdir ${tombmount} 2>/dev/null - return 1 + error "failure mounting the encrypted file" + losetup -d ${nstloop} + $norm || rmdir ${tombmount} 2>/dev/null + return 1 fi act "encrypted storage filesystem check" @@ -582,8 +592,8 @@ mount_tomb() { notice "encrypted storage $tombfile succesfully mounted on $tombmount" if ! [ $NOBIND ]; then - exec_safe_bind_hooks ${tombmount} - exec_safe_post_hooks ${tombmount} open + exec_safe_bind_hooks ${tombmount} + exec_safe_post_hooks ${tombmount} open fi return 0 } @@ -689,6 +699,9 @@ print "-----END PGP MESSAGE-----" } exec_safe_bind_hooks() { + if [[ -n ${(k)opts[-o]} ]]; then + MOUNTOPTS=${opts[-o]} + fi local MOUNTPOINT="${1}" local ME=${SUDO_USER:-$(whoami)} local HOME=$(grep $ME /etc/passwd | sed "s/^${ME}:.*:.*:.*:.*:\([\/a-z]*\):.*$/\1/" 2>/dev/null) @@ -1061,53 +1074,121 @@ EOF act "Tomb is now installed." } -main () { - echo $@ | grep '\-D' 2>&1 > /dev/null - # ????? - if [ $? = 0 ]; then +option_is_set() { + #First argument, the option (something like "-s") + #Second (optional) argument: if it's "out", command will print it out 'set'/'unset' + # This is useful for if conditions + #Return 0 if is set, 1 otherwise + [[ -n ${(k)opts[$1]} ]]; + r=$? + if [[ $2 == out ]]; then + if [[ $r == 0 ]]; then + echo 'set' + else + echo 'unset' + fi fi + return $r; +} +option_value() { + #First argument, the option (something like "-s") + echo ${opts[$1]} +} - ARGS=$@[@] - - OPTS=`getopt -o hvqDs:k:no: -n 'tomb' -- "$@"` - while true; do - case "$1" in - -h) - usage - exit 0 ;; - -v) - notice "Tomb - simple commandline tool for encrypted storage" - act "version $VERSION ($DATE) by Jaromil @ dyne.org" - # print out the GPL license in this file - act "" - cat ${TOMBEXEC} | awk 'BEGIN { license=0 } /^# This source/ { license=1 } { if(license==1) print " " $0 } -/MA 02139, USA.$/ { license=0 }' - act "" - exit 0 ;; - -q) QUIET=1; shift 1 ;; - -D) - print "[D] Tomb invoked with args \"${(f)@}\" " - print "[D] running on `date`" - DEBUG=1; shift 1 ;; - -s) SIZE=$2; shift 2 ;; - -k) KEY=$2; shift 2 ;; - -n) NOBIND=1; shift 1 ;; - -o) MOUNTOPTS=$2; shift 2;; - --) shift; break ;; - *) CMD=$1; - FILE=$2; MOUNT=$3; # compat with old args - CMD2=${2}; CMD3=${3}; break ;; - esac +main() { + local -A subcommands_opts + ### Options configuration + #Hi, dear developer! Are you trying to add a new subcommand, or to add some options? + #Well, keep in mind that: + # 1. An option CAN'T have differente meanings/behaviour in different subcommands. + # For example, "-s" means "size" and accept an argument. If you are tempted to add + # an option "-s" (that means, for example "silent", and doesn't accept an argument) + # DON'T DO IT! + # There are two reasons for that: + # I. usability; user expect that "-s" is "size + # II. Option parsing WILL EXPLODE if you do this kind of bad things + # (it will say "option defined more than once, and he's right) + main_opts=(q -quiet=q D -debug=D h -help=h v -verbose=v) + subcommands_opts[open]="n -nohook=n k: -key=k o: -mount-options=o" + subcommands_opts[mount]=${subcommands_opts[open]} + subcommands_opts[create]="s: -size=s" + subcommands_opts[close]="" + subcommands_opts[help]="" + subcommands_opts[slam]="" + subcommands_opts[list]="" + subcommands_opts[help]="" + subcommands_opts[bury]="" + subcommands_opts[exhume]="" + subcommands_opts[decompose]="" + subcommands_opts[recompose]="" + subcommands_opts[install]="" + subcommands_opts[askpass]="" + subcommands_opts[mktemp]="" + ### Detect subcommand + local -aU every_opts #every_opts behave like a set; that is, an array with unique elements + for optspec in $subcommands_opts$main_opts; do + for opt in ${=optspec}; do + every_opts+=${opt} + done done - - if ! [ $CMD ]; then - error "first argument missing, use -h for help" - exit 0 + local -a oldstar + oldstar=($argv) + zparseopts -M -E -D -Adiscardme ${every_opts} + unset discardme + subcommand=$1 + if [[ -z ${(k)subcommands_opts[$subcommand]} ]]; then #there's no such subcommand + error "Subcommand '$subcommand' doesn't exist" + exit 127 fi + argv=(${oldstar}) + unset oldstar + + ### Parsing global + command-specific options + # zsh magic: ${=string} will split to multiple arguments when spaces occur + set -A cmd_opts ${main_opts} ${=subcommands_opts[$subcommand]} + if [[ -n $cmd_opts ]]; then #if there is no option, we don't need parsing + zparseopts -M -E -D -Aopts ${cmd_opts} + if [[ $? != 0 ]]; then + error "Some error occurred during option processing. See \"tomb help\" for more info" + exit 127 + fi + fi + #build PARAM (array of arguments) and check if there are unrecognized options + ok=0 + PARAM=() + for arg in $*; do + if [[ $arg == '--' || $arg == '-' ]]; then + ok=1 + continue #it shouldnt be appended to PARAM + elif [[ $arg[1] == '-' ]]; then + if [[ $ok == 0 ]]; then + error "unrecognized option $arg" + exit 127 + fi + fi + PARAM+=$arg + done + #first parameter actually is the subcommand: delete it and shift + PARAM[1]=() + shift + ### End parsing command-specific options + + ### Set global options (useless, but for code retro-compatibility) + for opt in ${(k)global_opts}; do + if [[ $opt == '-q' ]]; then + QUIET=1 + elif [[ $opt == '-D' ]]; then + DEBUG=1 + fi + done + + CMD=$subcommand + CMD2=$PARAM[1] + CMD3=$PARAM[2] func "Tomb command: $CMD $CMD2 $CMD3" - case "$CMD" in + case "$subcommand" in create) check_priv ; create_tomb ;; mount) check_priv ; mount_tomb ;; open) check_priv ; mount_tomb ;; diff --git a/src/tomb-open b/src/tomb-open @@ -211,7 +211,7 @@ if [ $1 ]; then # is it a file? exit 1 else - "${TOMBEXEC}" -k ${tombkey} mount ${tombdir}/${tombfile} + "${TOMBEXEC}" mount -k ${tombkey} ${tombdir}/${tombfile} success=$? fi @@ -255,7 +255,7 @@ if [ "$1" != "wizard" ]; then if [ -z $DISPLAY ]; then error "tomb-open is a wrapper for the command 'tomb'" error "[!] type 'tomb-open wizard' if you want to be guided" - "${TOMBEXEC}" -h + "${TOMBEXEC}" help exit 1 fi fi @@ -338,7 +338,7 @@ cat <<EOF EOF tombfile=${tombname}.tomb -"${TOMBEXEC}" -s $tombsize create ${tombfile} +"${TOMBEXEC}" create -s $tombsize ${tombfile} if [ $? != 0 ]; then error "An error occurred creating tomb, operation aborted." @@ -368,7 +368,7 @@ if [ $? = 0 ]; then notice "${tombname}.key succesfully saved on your USB" act "now we'll proceed opening your brand new tomb" - "${TOMBEXEC}" -k ${tombfile}.key open ${tombfile} + "${TOMBEXEC}" open -k ${tombfile}.key ${tombfile} if [ $? = 0 ]; then launch_status ${tombname} fi @@ -392,7 +392,7 @@ cat <<EOF EOF -"${TOMBEXEC}" -k ${tombname}.tomb.key open ${tombfile} +"${TOMBEXEC}" open -k ${tombname}.tomb.key ${tombfile} if [ $? = 0 ]; then launch_status ${tombname} fi