diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-01-15 12:35:30 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-01-15 12:35:30 +0200 |
commit | 4c725525602884dab1298d9d02c679dad5335efd (patch) | |
tree | 6fd9f8445cff39e3a6f2893db9251d1159354cee /rex | |
parent | c0cfaa776c4d5953bd26d2e3039bfe422feb9f72 (diff) | |
download | rex-4c725525602884dab1298d9d02c679dad5335efd.tar.gz rex-4c725525602884dab1298d9d02c679dad5335efd.tar.bz2 |
New option --edit (-E) to edit the credentials database.
Diffstat (limited to 'rex')
-rwxr-xr-x | rex | 141 |
1 files changed, 136 insertions, 5 deletions
@@ -32,6 +32,7 @@ array set rexdb {} catch {set config(prompt) $env(EXPECT_PROMPT)} +# Return the value of the configuration option KEY proc config_option {key} { global config if {![info exists config(option,$key)]} { @@ -40,6 +41,8 @@ proc config_option {key} { return $config(option,$key) } +# rexdbget KEY [KEY...] +# Iterate over KEYs, find first of them that is defined and return its value. proc rexdbget {args} { global rexdb @@ -59,6 +62,7 @@ proc rexdbget {args} { } } +# rexdbput KEY VALUE [KEY VALUE...] proc rexdbput {args} { global rexdb @@ -82,6 +86,7 @@ proc rexdbput {args} { set rexdb(updated) 1 } +# rexdbclr KEY... proc rexdbclr {args} { global rexdb @@ -132,12 +137,14 @@ proc echo {a} { } } +# Print program usage instructions proc prusage {} { - global argv0 + global argv0 usrconfdir puts "usage: $argv0 \[OPTIONS\] \[COMMAND ARGS...\]" puts " or: $argv0 \[OPTIONS\] -c SRC... DST" puts " or: $argv0 \[sudo\] shell HOST" + puts " or: $argv0 --editdb \[FILE\]" puts " or: $argv0 -e \[WORD...\]" puts "" puts "Rex executes a command on several hosts." @@ -146,12 +153,15 @@ proc prusage {} { -d, --debug increase debugging level -H, --host NAME add host to the list + -E, --editdb [FILE] + edit the database file (default ~/.rex/db) -e, --encrypt [WORD] encrypt the WORD (read it from the stdin if not supplied), and print out the result -N, --noop ignore all commands (useful for side effects) -u, --user NAME log in as user NAME - -p, --pass PWD set password (unsafe!) + -p, --password PWD + set password (unsafe!) -c, --cp, --copy copy arguments to each host (see the 2nd form above) -C, --config FILE read config from FILE --list-groups list available host groups @@ -171,6 +181,7 @@ Report bugs to <gray+rex@gnu.org.ua> exit 0 } +# Print program version and copyleft info. proc prversion {} { global version @@ -183,6 +194,9 @@ There is NO WARRANTY, to the extent permitted by law. exit 0 } +# getans [-echo] WORDS... +# Concat WORDS into a prompt, display it, read the user's input from stdin +# and return it. The -echo option turns echo off (for inputting passwords). proc getans {args} { if {[lindex $args 0] == "-echo"} { set noecho 1 @@ -204,6 +218,9 @@ proc getans {args} { return $retval } +# getyn WORDS +# Same as getans, but restrict user input to Y, N, and <CR>. Return true +# if the user replied Y (or <CR>), and false otherwise. proc getyn {args} { lappend args { [Y/n]?} switch -glob [string trimleft [getans [eval concat $args]] " \t"] { @@ -213,21 +230,26 @@ proc getyn {args} { } } +# Encrypt password proc passenc {pass} { binary scan [encoding convertto ebcdic $pass] H* enc return $enc } +# Decrypt password proc passdec {code} { encoding convertfrom ebcdic [binary format H* $code] } +# Read rex database FILE into VAR proc readdb {file var} { upvar $var x debug 2 reading database file $file set fd [open $file "r"] + set lnum 0 while {[gets $fd line] >= 0} { + incr lnum regsub {[ \t]*#.*} [string trimright $line] "" line if [regexp {(.+)[ \t]+(.*)} "$line" dummy key val] { set x($key) $val @@ -236,6 +258,7 @@ proc readdb {file var} { close $fd } +# Auxiliary function to compare two keys proc keycmp {a b} { foreach ka [split $a ":"] kb [split $b ":"] { set x [string compare $ka $kb] @@ -245,7 +268,8 @@ proc keycmp {a b} { } return 0 } - + +# Write rex database from variable VAR into FILE. proc writedb {file var} { upvar $var x @@ -257,7 +281,8 @@ proc writedb {file var} { close $fd file rename -force ${file}.tmp $file } - + +# Update rexdb if it has been modified. proc updatedb {} { global rexdb confpath @@ -268,6 +293,95 @@ proc updatedb {} { } } +# Prepare a temporary "database view" file. +# dbname - name of the original file +# tempfile - temporary output file name +# dbvar - array variable to get key/value pairs from. +proc mkdbview {dbname tempfile dbvar} { + upvar $dbvar db + set fd [open $tempfile w] + puts $fd "# You are editing file $dbname" + puts $fd [format "# %-30.30s\t%s" "Key" "Value"] + foreach key [lsort -command keycmp [array names db]] { + if {$key == "pass" || + [string last ":pass" $key] == [expr [string length $key] - 5]} { + puts $fd [format "%-32.32s\t%s" $key [passdec $db($key)]] + } else { + puts $fd [format "%-32.32s\t%s" $key $db($key)] + } + } + close $fd +} + +# Save modified dbview from tempfile into outfile +proc svdbview {tempfile outfile} { + array set x {} + readdb $tempfile x + foreach key [array names x] { + if {$key == "pass" || + [string last ":pass" $key] == [expr [string length $key] - 5]} { + set x($key) [passenc $x($key)] + } + } + writedb $outfile x +} + +# Ask user about his further intentions. +proc whatnow {} { + while true { + set reply [getans {What now ([s]ave, [q]uit, [e]dit again)?}] + switch -nocase $reply { + s - + sa - + sav - + save { return "s" } + q - + qu - + qui - + quit { return "q" } + e - + ed - + edi - + edit { return "e" } + } + } +} + +# Edit database file dbname. The file is formatted in a more or less +# human-readable way, stored in a temporary file and an editor is started +# on that file. +proc editdb {dbname} { + array set rexdb {} + + readdb $dbname rexdb + + if [info exist env(VISUAL)] { + set ed $env(VISUAL) + } elseif [info exist env(EDITOR)] { + set ed $env(EDITOR) + } else { + set ed "vi" + } + + set tempfile ".rex.[pid].tmp" + exec "/bin/sh" -c "umask 077; touch $tempfile" + + mkdbview $dbname $tempfile rexdb + while 1 { + exec $ed $tempfile + switch [whatnow] { + e continue + q break + s { + svdbview $tempfile $dbname + break + } + } + } + file delete $tempfile +} + +# Return user name for the given host. proc hostuser {host} { global config @@ -298,6 +412,7 @@ proc hostuser {host} { error "no default username (shouldn't happen)" } +# Return user password for the given host proc hostpass {host} { global rexdb config @@ -329,6 +444,7 @@ proc hostpass {host} { return "" } +# List available host groups. proc listgroups {} { global confpath argv0 @@ -548,7 +664,7 @@ proc getopt {args} { return 0 } -set shortopts "Ccdeig:HhlNp:Ss:u:x:Vw" +set shortopts "CcdeEig:HhlNp:Ss:u:x:Vw" set longopts { debug d host H @@ -568,6 +684,7 @@ set longopts { confirm w list-groups - noop N + editdb E } set optind 0 @@ -602,6 +719,7 @@ getopt -progname $argv0 -longopts $longopts $argc $argv $shortopts { list-groups { listgroups exit 0 } + E { set config(option,editdb) 1 } h prusage V prversion default { exit 1 } @@ -611,6 +729,19 @@ getopt -progname $argv0 -longopts $longopts $argc $argv $shortopts { set argv [lrange $argv $optind end] set argc [expr $argc - $optind] +if [config_option editdb] { + if {$argc >= 1} { + set dbname [lindex $argv 0] + } elseif [file readable "$usrconfdir/db"] { + set dbname "$usrconfdir/db" + } else { + puts stderr "$0: no db file to edit" + exit 1 + } + editdb $dbname + exit 0 +} + if [info exists config(option,hostgroup)] { debug 1 "using hostgroup $config(option,hostgroup)" set libpath [linsert libpath 0 \ |