1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
|
#!/bin/sh
# vim: set filetype=sh :
# file: /usr/share/shellia/ia.highlevel
# copyright: Bernd Schumacher <bernd.schumacher@hpe.com> (2007-2021)
# license: GNU General Public License, version 3
# description: ia.higlevel - highlevel functions for shellia
# ia_find_lib <lib>
# example:
# program "/nfs/host1/usr/local/bin/program" with line "ia_source_lib /usr/share/shellia/ia" called
# from directory "/tmp" will try "/tmp/ia", ... "/nfs/host1/usr/share/shellia/ia", ... and
# "/usr/share/shellia/ia" in the listed order.
ia_find_lib()
{
local i
local p
for i in "$@"; do
[ -f "./$(basename "$i")" ] && /bin/echo "./$(basename "$i")" && return 0
p="$0"
while :; do
[ "$p" != "$(dirname p)" ] || break
p="$(dirname "$p")"
[ -f "$p/$i" ] && /bin/echo "$p/$i" && return 0
done
[ -f "$i" ] && /bin/echo "$i" && return 0
return 1
done
}
if [ "${ia_prefix:=ia}" != "ia" ] || [ "${dbg_prefix:=dbg}" != "dbg" ]; then
[ -n "$(eval "/bin/echo \${${ia_prefix}_highlevel_already_sourced:=}")" ] && return
eval "$(sed -e "s/ia_/${ia_prefix}_/g" -e "s/ia()/${ia_prefix}()/g" \
-e "s/dbg/${dbg_prefix}/g" "$(ia_find_lib /usr/share/shellia/ia.log)")"
return
fi
[ -n "${ia_highlevel_already_sourced:=}" ] && return || ia_highlevel_already_sourced=1
. "$(ia_find_lib /usr/share/shellia/ia.log)"
ia_inherit_vars="ia_direct_stderr_fd ia_inherit_vars ia_log_fd ia_red ia_green ia_orange ia_cyan ia_no_color ia_use_silent"
# ia_red ia_green ia_orange ia_cyan ia_no_color: to respect option -a with ia_ssh
# ia_use_silent: to respect option -s with ia_ssh
# ia_add "ia_file <shellia-script>"
ia_file()
{
local ia_prefix_head
(
ia_prefix_head="${ia_head}:"
eval "export ia_prefix_head $ia_inherit_vars"
"$@"
)
}
#res="$(ia_file /bin/ls -d / "/ /" / / 2>/dev/null)" # check args with spaces
#exp="$(/bin/echo -e "/\n/\n/")"
#[ "$res" = "$exp" ] && echo "ia_file-1 ok" || echo "ia_file-1 error res=<$res> exp=<$exp>"
#exit 0
# ia_add "ia_ssh <ssh-options> [--no-ia-fd] [--fd <fd>] [--vars <vars>] <destination> [<shellia-script> ...]"
#
# ia_ssh transfers additional to the filedescriptors stderr and stdout the shellia
# filescriptors $ia_direct_sderr_fd and $ia_log_fd with ssh, if called without --no-ia-fd.
# Also the variables listed in $ia_inherit_vars are provided to the remote shellia-script.
#
# With $ia_log_fd the remote shellia-script does not need to use an own logfile
# and the log can be written to the local logfile.
#
# The order of data transferred by filedescriptors may change within a single shellia step.
#
# <ssh-options> ssh options described in ssh(1)
# <fd> the filedescriptors <fd> will be transfered
# <vars> the variables <vars> will be provided
# <destination> ssh destination described in ssh(1)
# <shellia-script> script that can use the transferred filedescriptors
# and variables
ia_ssh()
{
local cmd destination i no_ia_fd
dbg "ia_ssh called with *=<$*>"
cmd="ia_ssh_fd"
no_ia_fd=""
while [ $# -gt 0 ]; do
if [ "$1" = "--no-ia-fd" ]; then
no_ia_fd="$1"
shift
elif [ -n "$(/bin/echo "$1" | grep "^-[46AaCfGgKkMNnqsTtVvXxYy]$")" ]; then
cmd="$cmd $1"
shift
elif [ -n "$(/bin/echo "$1" | grep "^-[bcDEeFIiJLlmOopQRSWw]$")" -o \
-n "$(/bin/echo " --vars --fd " | grep " $1 ")" ]; then
cmd="$cmd $1 \"$(/bin/echo "$2"|ia_easy_backslash)\""
shift 2
else
destination="$1"
shift
break
fi
done
cmd="$cmd --vars \"ia_prefix_head=\\\"${ia_head}/$destination:\\\"\""
for i in $ia_inherit_vars; do
cmd="$cmd --vars \"$i=\\\"$(eval /bin/echo "\$$i")\\\"\""
done
[ -n "$no_ia_fd" ] || cmd="$cmd --fd \"${ia_direct_stderr_fd}\" --fd \"${ia_log_fd}\""
dbg "ia_ssh will call: cmd=<$cmd> destination=<$destination>"
eval "$cmd $destination \"eval $(ia_prep_args4eval "$@"|ia_easy_backslash)\""
}
# ia_ssh_fd <ia_ssh_fd-options> <standard-options>
#
# ssh normally only transfers data from the filedescriptors stdin, stdout and
# stderr. With ia_ssh_fd data from additional filedescriptors can be
# transferred. Before transferring, data from additional filedescriptors will
# be marked and redirected to stdout. After transferring, data will be
# extracted from stdout and redirected to filedescriptors again.
#
# <ia_ssh_fd-options>:
# [--fd <fd>]
# <fd> is a filedescriptor between 3 and 9, that has to be transferred
# with ssh. It will be merged to stdout for ssh transfer and extracted
# again after transfer. This option can be given multiple times.
# [--vars <vars>]
# this allows to set variables on the remote host. <vars> should be
# a string to be valuated with the command eval.
#
# <standard-options>:
# [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface] [-b bind_address]
# [-c cipher_spec] [-D [bind_address:]port] [-E log_file]
# [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file]
# [-J destination] [-L address] [-l login_name] [-m mac_spec]
# [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]
# [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] destination
# [command]
# see ssh(1)
#
ia_ssh_fd()
{
local fd options vars fdt destination script
fd=""
options=""
vars=""
[ -n "${ssh:-}" ] || ssh="ssh"
while [ $# -gt 0 ]; do
if [ "$1" = "--fd" ]; then
[ -n "$(/bin/echo "$2" | grep "^[3-9]$")" ] ||
{ /bin/echo "error --fd <$2> not between 3 and 9" >&2; return 1; }
if [ -z "$(/bin/echo " $fd " | grep " $2 ")" ]; then
[ -n "$fd" ] && fd="$fd $2" || fd="$2"
fi
shift 2
elif [ "$1" = "--vars" ]; then
[ -n "$vars" ] && vars="$vars $2" || vars="$2"
shift 2
elif [ -n "$(/bin/echo "$1" | grep "^-[46AaCfGgKkMNnqsTtVvXxYy]$")" ]; then
options="$options $1"
shift
elif [ -n "$(/bin/echo "$1" | grep "^-[bcDEeFIiJLlmOopQRSWw]$")" ]; then
options="$options $1 \"$2\""
shift 2
else
destination="$1"
shift
break
fi
done
# if needed fd does not exist, create and send to /dev/null
for i in $fd; do
[ true 2>/dev/null <&${i} ] || eval "exec ${i}>/dev/null"
done
{
# find free temporary filedescriptor
for fdt in $(seq 3 9); do
[ true <&${fdt} ] || break
fdt=""
done 2>/dev/null
dbg "options=<$options> destination=<$destination> fd=<$fd> fdt=<$fdt> vars=<$vars> *=<$*>"
script="export ia_ssh_running=1
# _ia_ssh_fd_mark <fd> [...] <fdt> <cmd>
# is a recursiv function used internally by ia_ssh_fd on the remote host
# <fd> will be transferred with ssh
# <fdt> is used to temporarily swap filedescriptor <fd> with stdout
_ia_ssh_fd_mark()
{
local fd fdt
if [ \$# -eq 2 ]; then
# run cmd and mark stdout with 1
$([ -n "$vars" ] && echo "eval \"export $(/bin/echo "$vars"|ia_easy_backslash)\"" || echo "# no vars")
{ sh -c \"\$2\"; /bin/echo \"ia-_-retval \$?\"; } | sed -u \"/^ia-_-retval/ ! s/^/1/\"
else
# temporary switch \$fd with stdout to mark it with \$fd
fd=\"\$1\"
fdt=\"\$2\" # use to swap
shift
eval \"exec \$fd>&1\"
{
eval \"exec \$fdt>&1 1>&\$fd \$fd>&\$fdt \$fdt>&-\"
_ia_ssh_fd_mark \"\$@\"
} | sed -u \"s/^/\$fd/\"
fi
}
eval \"_ia_ssh_fd_mark $fd $fdt $(ia_prep_args4eval "$@"|ia_easy_backslash)\""
eval "$ssh $options \"$(/bin/echo "$destination"|ia_easy_backslash)\" \"$(/bin/echo "$script"|ia_easy_backslash)\"" ||
/bin/echo "ia-_-retval $?" # ssh itself did not work
} | gawk '/^ia-_-retval / {retval=$2; next} # first word ia-_-retval means exit code follows
{d=sprintf("/dev/fd/%s",substr($0,1,1)) # first char of line is device number
if (d=="-") {exit $d}
if (sub(/ia_ssh_no_nl/,"")) {printf("%s",substr($0,2)) >d} # print no newline, if ia_ssh_no_nl
else {print substr($0,2) >d}; fflush()} END {exit retval}'
}
##dbg() { echo "dbg: $*" >&2; }
#dbg() { :; }
#if [ ! "$no_test_in_test" ]; then
# so="-o PreferredAuthentications=hostbased,publickey"
# ssh $so localhost : || { echo "error: need password-less ssh to localhost for this tests" >&2; exit 1; }
# res="$(ia_ssh_fd $so --fd 6 localhost "echo hallo1-6 >&6" 6>&1)"
# res="$res $?"
# exp="hallo1-6 0"
# [ "$res" = "$exp" ] && echo "ia_ssh_fd-1 ok" || echo "ia_ssh_fd-1 error res=<$res> exp=<$exp>"
# res="$(ia_ssh_fd $so --vars "hi1=hallo2-1 hi2=hallo2-2" localhost "echo \$hi1; echo \$hi2 >&2" 2>&1 | grep -v "^dbg:" | sort)"
# res="$res $?"
# exp="$(/bin/echo -e "hallo2-1\nhallo2-2") 0"
# [ "$res" = "$exp" ] && echo "ia_ssh_fd-2 ok" || echo "ia_ssh_fd-2 error res=<$res> exp=<$exp>"
# res="$(ia_ssh_fd $so --fd 6 --fd 3 localhost "echo hallo3-6 >&6; echo hallo3-3 >&3" 6>&1 3>&1 | sort)"
# res="$res $?"
# exp="$(/bin/echo -e "hallo3-3\nhallo3-6") 0"
# [ "$res" = "$exp" ] && echo "ia_ssh_fd-3 ok" || echo "ia_ssh_fd-3 error res=<$res> exp=<$exp>"
# res="$(ia_ssh_fd $so -A --vars "no_test_in_test=1" --fd 4 localhost ". $(realpath $0); ia_ssh_fd $so --fd 4 localhost 'echo hallo4-4 >&4'" 4>&1)"
# res="$res $?"
# exp="hallo4-4 0"
# [ "$res" = "$exp" ] && echo "ia_ssh_fd-4 ok" || echo "ia_ssh_fd-4 error res=<$res> exp=<$exp>"
# res="$(ia_ssh_fd $so --fd 6 localhost "/bin/ls -d / \"/ /\" / / 2>/dev/null")" # ls "/ /" (not existing) exits with 2
# res="$res $?"
# exp="$(/bin/echo -e "/\n/\n/") 2"
# [ "$res" = "$exp" ] && echo "ia_ssh_fd-5 ok" || echo "ia_ssh_fd-5 error res=<$res> exp=<$exp>"
# res="$(ia_ssh_fd $so --fd 6 baduser@localhost "echo hallo1-6 >&6" 6>&1)"
# res="$res $?"
# exp=" 255"
# [ "$res" = "$exp" ] && echo "ia_ssh_fd-6 ok" || echo "ia_ssh_fd-6 error res=<$res> exp=<$exp>"
# res="$(ia_ssh_fd $so localhost "echo err; exit 13")"
# res="$res $?"
# exp="err 13"
# [ "$res" = "$exp" ] && echo "ia_ssh_fd-7 ok" || echo "ia_ssh_fd-7 error res=<$res> exp=<$exp>"
# exit 0
#fi
|