aboutsummaryrefslogtreecommitdiff
path: root/rex
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2013-01-15 12:35:30 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2013-01-15 12:35:30 +0200
commit4c725525602884dab1298d9d02c679dad5335efd (patch)
tree6fd9f8445cff39e3a6f2893db9251d1159354cee /rex
parentc0cfaa776c4d5953bd26d2e3039bfe422feb9f72 (diff)
downloadrex-4c725525602884dab1298d9d02c679dad5335efd.tar.gz
rex-4c725525602884dab1298d9d02c679dad5335efd.tar.bz2
New option --edit (-E) to edit the credentials database.
Diffstat (limited to 'rex')
-rwxr-xr-xrex141
1 files changed, 136 insertions, 5 deletions
diff --git a/rex b/rex
index 9a5a134..d6d779d 100755
--- a/rex
+++ b/rex
@@ -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 \

Return to:

Send suggestions and report system problems to the System administrator.