Listing 2 Triplet (SX.AWK, CX.AWK, C2X.AWK) for implementing
a Remote Execution Capability. NETGZ: Sent by CP.AWK via C2X.AWK
and invoked by SX.AWK, a sample script that waits for and untars
a directory tree.
NETGZ -----------------------------------------------------------------
#!/bin/bash
. $SCR/bash.i
argc $# 4 "w.netgz ^y.hostid ^m.xfrlog ^tarball rpar rdir"
assert file $3
#----------------------------------------
gethost $1
hostid=$RET
xfrlog=$2
localgz=$3
tball=${3##*/}
bn=${tball%.*}
parentdir=$4
newdir=$5
if [ "${tball: -3:3}" = "tar" ]; then
decomp="xpf"
else
decomp="zxpf"
fi
# To avoid collisions, scripts must have unique names.
# Embed this script's process id. Not foolproof, but reasonable.
myhost=$(hostname)
remotegz=$parentdir/$tball
log=/var/log/netcp-$bn-$$
done=/var/run/$tball.DONE
localscr=$SCR/netcp-$bn-$$.bash
remotescr=/var/run/netcp-$bn-$$.bash
ini=$SCR/netcp-$bn-$$.ini
pid=/var/run/netcp-$bn-$$.pid
#----------------------------------------
# Get the size in bytes of the file being transferred
a=($(ls -l $localgz))
let size=${a[4]}
# Because awk began life as a text-processing tool, there are
# yet problems with final newlines.
let size--
# Calculate a waiting period based on the size of the file.
# This number was derived empirically.
let incr=25000000
if [ $size -lt $incr ]; then
let secs=5
else
secs=$((size/incr))
fi
cat <<EOF > $localscr
echo \$$ > $pid
# Wait for the tarball to be completely transferred.
let size=0
while [ \$size -lt $size ]; do
sleep $secs
a=(\$(ls -l $parentdir/$tball))
let size=\${a[4]}
done
# Backup the remote directory, if it already exists.
cd $parentdir
if [ -d $newdir ]; then
n=1
while [ -d $newdir.\$n ]; do let n=\$n+1; done
mv $newdir $newdir.\$n
fi
# Untar the tarball, creating a new directory tree.
tar $decomp $tball
rm $tball
rm $pid
rm $remotescr
# FreeBSD cleanup
chmod og-w $newdir 2> /dev/null
# This script signals completion to the remote client by sending back to
# /var/run on the remote host a file with the tarball's basename + ".DONE"
echo DONE > $done
cp.awk $myhost u+rw t $done
EOF
cat <<EOF > $ini
WAIT 2
LOG $log
EXE s $localscr $remotescr
AUX b u+rw $localgz $remotegz
EOF
echo ""
cat $ini
echo ""
x "c2x $hostid $ini" -c
C2X.AWK --------------------------------------------------------------
#!/bin/awk -f
# This script invokes CX.AWK
# Usage: c2x.awk ip-addr inifile
#
# The ini file contains a single instance each of 3 mandatory keywords:
#
# - WAIT ->
# arg 1: the number of seconds to wait between copies.
# - EXE ->
# arg 1: flag, ?/b for a script or binary executable
# arg 2: name of local script/binary to transmit to the remote host
# arg 3: complete pathname of script/binary to create on the remote
# host and then execute
# - LOG ->
# arg 1: local file into which to copy output from the remote executable
#
# The ini file contains multiple instances of these keywords:
#
# - AUX ->
# arg 1: type of file ?/b for text or binary
# arg 2: permissions string for the copied file
# arg 3: local auxiliary file needed by the executable named by EXE
# arg 4: complete pathname of auxiliary file to create on the
# remote host
function parseini (ini) {
i=2
loc[1]=loc[2]="*"
rmt[1]=rmt[2]="*"
prm[1]=prm[2]="*"
typ[1]=typ[2]="*"
wait = -1
while ((getline < ini) > 0) {
if ($1=="WAIT") {
wait=$2
continue
}
if ($1=="LOG") {
if (NF < 2) {
print (ini ": LOG keyword requires arg")
return 0
}
loc[1] = $2
continue
}
if ($1=="EXE") {
if (NF < 4) {
print (ini ": EXE keyword requires 3 args")
return 0
}
if ($2=="b")
prm[2]="u+x"
else
prm[2]="u+rw"
typ[2] = $2
loc[2] = $3
rmt[2] = $4
continue
}
if ($1=="AUX") {
if (NF < 5) {
print (ini ": AUX keyword requires 4 args")
return 0
}
++i
typ[i] = $2
prm[i] = $3
loc[i] = $4
rmt[i] = $5
continue
}
}
if (wait==-1) {
print ("No WAIT keyword in " ini)
return 0
}
if (loc[1]=="*") {
print ("No LOG keyword in " ini)
return 0
}
if (loc[2]=="*") {
print ("No EXE keyword in " ini)
return 0
}
filecnt = i
return 1
}
function dump() {
for (i=1; i<=filecnt; ++i) {
print ("wait\t" wait)
print ("loc[" i "]\t" loc[i])
print ("rmt[" i "]\t" rmt[i])
print ("prm[" i "]\t" prm[i])
print ("typ[" i "]\t" typ[i])
}
}
BEGIN {
if (ARGC<3) {
print "c2x.awk remotehost inifile"
print "ex: c2x.awk galleon c2x.ini"
# 0 1 2
} else {
if (!parseini(ARGV[2])) exit 0
for (i=2; i<=filecnt; ++i) {
sys = sprintf ("cp.awk %s %s %s %s \
%s",ARGV[1],prm[i],typ[i],loc[i],rmt[i])
print sys
system (sys)
if (wait) system ("sleep " wait)
}
sys = sprintf ("cx.awk %s %s %s %s",ARGV[1],typ[2],rmt[2],loc[1])
print sys
system (sys)
system ("date >> /var/log/c2x")
l = sprintf("/cmn/scr/c2x.awk %s %s",ARGV[1],ARGV[2])
print l >> "/var/log/c2x"
}
}
CX.AWK -----------------------------------------------------------------
#!/bin/awk -f
# cx.awk remote-host ftype remote-executable [local-output-file]
#
# This client that transmits one item:
# 1. Two fields separated by space:
# - ?/b for script or binary executable
# - The name of the script/binary on the remote host.
#
# This client immediately receives back from the remote server one item:
# 1. Output from the executable.
#
# This client pairs with server SX.AWK
BEGIN {
if (ARGC<5) {
print "cx.awk remote-host ?/b remote-executable [local-output-file]"
print "cx.awk galleon s /root/scr/netscr /var/log/netscr.log"
# 0 1 2 3 4
} else {
getline < "/var/run/sx.port"
if (NF==0) {
print "No file: /var/run/sx.port"
exit 1
}
Net = ("/inet/tcp/0/" ARGV[1] "/" $1)
if (ARGC<5) ARGV[4]="/var/log/cx.outp"
print (ARGV[2] " " ARGV[3]) |& Net
while ((Net |& getline rec) > 0) print rec > ARGV[4]
close (Net)
system ("date >> /var/log/cx")
l = sprintf("/cmn/scr/cx.awk %s %s %s %s",ARGV[1],ARGV[2],ARGV[3],ARGV[4])
print l >> "/var/log/cx"
}
}
SX.AWK -----------------------------------------------------------------
#!/bin/awk -f
# sx.awk PORT_NUMBER &
#
# This server receives the following item from the client:
# 1. Two fields separated by space:
# - A flag, ?/b, indicating whether the executable is script or binary
# - The name of an executable on the remote host to execute.
#
# The server passes nothing back. It is expected that, if completion
# should be signaled back to the remote client, the executable
# will perform that function. The sender knows its own host name, easing
# notification.
#
# This server pairs with client CX.AWK.
BEGIN {
system ("echo " ARGV[1] " > /var/run/sx.port")
Net = ("/inet/tcp/" ARGV[1] "/localhost/0")
Net |& getline
if (NF < 2) {
msg = sprintf("sx.awk: \"%s\"\nsx.awk ?/b executable",$0)
print msg
} else {
ftype = $1
exe = $2
if (ftype=="b" ) {
# Execute the binary and send back its stdout output.
while ((exe | getline) > 0) print $0 |& Net
} else {
# By convention, the shell to use is passed as the final dot-extension.
cnt = split (exe,arr,".")
if (arr[cnt]=="bash") {
sys = ("bash < " exe " &")
} else if (arr[cnt]=="sh") {
sys = ("sh < " exe " &")
} else {
sys = sprintf ("echo \"sx.awk: unknown shell: %s\"",arr[cnt])
}
system (sys)
}
}
close (Net)
system ("/cmn/scr/sx.awk " ARGV[1] " &")
system ("date >> /var/log/sx")
l = sprintf ("/cmn/scr/sx.awk %d %s %s",ARGV[1],ftype,exe)
print l >> "/var/log/sx"
}
|