diff --git a/scripts/upload.sh b/scripts/upload.sh new file mode 100644 index 00000000..ed52d9d2 --- /dev/null +++ b/scripts/upload.sh @@ -0,0 +1,362 @@ +#!/bin/sh +######################################################################## +# # +# generic utiltiy modules # +# (c) 2001 jannet it services # +# contact@jannet.de # +# # +# $Id$ +# # +# This program is free software; permission to use, copy, modify, # +# distribute, and sell this software and its documentation under the # +# terms of the GNU Public license as published by the Free Software # +# Foundation, either version 2 or any later version of the license, is # +# hereby granted without fee, provided that (i) the above copyright # +# notices and this permission notice appear in all copies of the # +# software and related documentation, and (ii) the name of JanNet may # +# not be used in any advertising or publicity relating to the software # +# without the specific, prior written permission of JanNet. # +# # +# This program is distributed in the hope that it will be useful, but # +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- # +# TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # +# Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software Founda- # +# tion, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # +# # +######################################################################## + + +MYNAME=`basename $0` +HOST=pkg.janware.com +CFG_FILE=$HOME/.uploadrc + +login=`whoami` +method= +pw_ftp= +pw_scp=$HOME/.ssh/identity +pw_rsync= +pw_rsync_ssh=$HOME/.ssh/identity +file_mode= +dir_mode= +file_owner= +file_group= +ssh_identity= + +test -f $CFG_FILE && . $CFG_FILE + +if [ -n "$ssh_identity" ]; then + CFG_IDENTITY="-i $ssh_identity" +fi + +usage() +{ +cat << EOT + + Unified Upload Interface + Version '$Revision$' - '$Date$' + (c) 2002 Jannet IT Services http://www.jannet.de + + usage: $MYNAME -h + $MYNAME [-v] [-c variable] source [method://][[password:]login@]host[:target[:mode[:dirmode[:owner[.group]]]]] ... + + where "method" is one of "ftp", "scp", "rsync", "rsync_ssh". + + "password" may be a valid rsa private key file, too, if this makes sense with the chosen method. + + "source" may be a directory or a file. If it ends with a slash, it is treated as an existing + directory and expands to its contents. Thus, in the following, "source" denotes either a single + or multiple files or directories. + + "target" may be a directory or a file. + + If it is an existing directory, it will be populated with "source". The name(s) of "source" will be preserved. + + If it is not an existing directory, and does not have a trailing slash, + $MYNAME will skip everything following the last slash for a directory name, create + it, and populate it with "source". "source" will get renamed to the last portion of "target". + + If it is not an existing directory and has a trailing slash it will be created and populated with + "source". + + The default values for the optional parameters can be redefined in $CFG_FILE. + Currently they are: + + login="$login" + method="$method" + pw_ftp="$pw_ftp" + pw_scp="$pw_scp" + pw_rsync="$pc_rsync" + pw_rsync_ssh="$pw_rsync_ssh" + file_mode="$file_mode" + dir_mode="$dir_mode" + file_owner="$file_owner" + file_group="$file_group" + ssh_identity="$ssh_identity" + + Options: + + -h : show this help screen + -c variable : show contents of variable + valid variables are login, method, passwd, file_owner, file_group, file_mode, dir_mode, + source, target_file + -p : parse command line into variables, show results and exit if -x switch is active + -x : exit after informational commands + +EOT + [ -n "$1" ] && exit $1 +} + +log() +{ + echo $@ +} + +err() +{ + log $@ +} + +fatal() +{ + err $@ + exit 1 +} + +is_dir() +{ + isdir="`echo $1 | sed 's/.*\/$/yes/'`" + test "$isdir" = yes +} + +parse_target() +{ + target=$1 + LOGIN=`echo $target | sed -e 's/^.*:\/\///; /@/ !d; s/@.*//; s/.*://'` + test -n "$LOGIN" && echo login="\"$LOGIN\";" + METHOD=`echo $target | sed -e '/:\/\// !d; s/:\/\/.*//'` + test -n "$METHOD" && echo method="\"$METHOD\";" + PASSWD=`echo $target | sed -e '/@/ !d; /@/ !d; s/^.*:\/\///; s/@.*//' | rev | cut -d: -f2 | rev` + if [ -n "$PASSWD" ]; then + echo passwd="\"$PASSWD\";" + if [ -f "$PASSWD" ]; then + echo IDENTITY="\"-i $PASSWD\";" + else + echo IDENTITY="\"$CFG_IDENTITY\";" + fi + echo pw_ftp="\"$PASSWD\";" + echo pw_scp="\"$PASSWD\";" + echo pw_rsync="\"$PASSWD\";" + echo pw_rsync_ssh="\"$PASSWD\";" + fi + HOST=`echo $target | sed -e 's/^.*:\/\///; s/.*@//' | cut -d: -f1` + case $METHOD in + rsync_ssh) + if [ -z "$HOST" ]; then + PARSE_TARGET=FALSE + return + fi + echo host="\"$HOST\";" + ;; + "") + ;; + *) + PARSE_TARGET=FALSE + return + ;; + esac + TARGET_FILE=`echo $target | sed -e 's/^.*:\/\///; s/.*@//' | cut -d: -f2` + if [ -n "$TARGET_FILE" ]; then + echo target_file="\"$TARGET_FILE\";" + else + PARSE_TARGET=FALSE + return + fi + FILE_MODE=`echo $target | sed -e 's/^.*:\/\///; s/.*@//; /:/ !d' | cut -d: -f3` + test -n "$FILE_MODE" && echo file_mode="\"$FILE_MODE\";" + DIR_MODE=`echo $target | sed -e 's/^.*:\/\///; s/.*@//; /:/ !d' | cut -d: -f4` + test -n "$DIR_MODE" && echo dir_mode="\"$DIR_MODE\";" + FILE_OWNER=`echo $target | sed -e 's/^.*:\/\///; s/.*@//; /:/ !d' | cut -d: -f5 | cut -d. -f1` + test -n "$FILE_OWNER" && echo file_owner="\"$FILE_OWNER\";" + FILE_GROUP=`echo $target | sed -e 's/^.*:\/\///; s/.*@//; /:/ !d' | cut -d: -f5 | cut -d. -f2` + test -n "$FILE_GROUP" && echo file_group="\"$FILE_GROUP\";" + + if is_dir "$target_file" ; then + echo target_path=\"$TARGET_FILE/`basename $TARGET_FILE`\" # TODO: this is bullshit + else + echo target_path=\"$TARGET_FILE\" # TODO: this is bullshit + fi + + echo "PARSE_TARGET=\"OK\";" +} + +ssh_exec_stdin() +{ + $SSH -l $login $IDENTITY $host -C "SCRIPT=\`mktemp /tmp/$MYNAME""_XXXXXX\`; cat > \$SCRIPT; /bin/sh \$SCRIPT; rm \$SCRIPT" +} + +ssh_mkdir() +{ + set -e + dir=`echo $1 | sed -e 's/\/[^\/]*$//; s/\/*$//'` + parts="/ `echo $dir | sed -e 's%/% %g'`" + test -n "$dir_mode" && MODE="-m $dir_mode" +cat << EOT | + for part in $parts; do + path="\$path/\$part" + if [ ! -d \$path ]; then + mkdir \$path || break + test -n "$2" && chown "$2" \$path || continue + test -n "$3" && chgrp "$3" \$path || continue + test -n "$4" && chmod "$4" \$path || continue + fi + if [ ! -d \$path ]; then + fatal "failed to create directory \$path, owner=\$file_owner, group=\$file_group, mode=\$dir_mode" + fi + done +EOT + ssh_exec_stdin +} + +ssh_chown() +{ + set -e + dir=`echo $1 | sed -e 's/\/[^\/]*$//; s/\/*$//'` +cat << EOT | + if [ -f "$1" -a -n "$file_owner" ]; then chown $file_owner $1; fi + if [ -f "$1" -a -n "$file_group" ]; then chgrp $file_group $1; fi + #cd $1 + #if [ -n "$2" ]; then chown -R $2 . ; fi + #if [ -n "$3" ]; then chown -R $3 . ; fi +EOT + ssh_exec_stdin +} + +ssh_chmod() +{ + set -e + dir=`echo $1 | sed -e 's/\/[^\/]*$//; s/\/*$//'` +cat << EOT | + if [ -f "$1" -a -n "$file_mode" ]; then chmod $file_mode $1; fi + #if [ -n "$2" ]; then find . -type f | xargs --no-run-if-empty chmod $2 ; fi + #if [ -n "$3" ]; then find . -type d | xargs --no-run-if-empty chmod $3 ; fi +EOT + ssh_exec_stdin +} + +# -- here we go +# -- command line arguments +set -- `getopt 'hvc:px' $*` +while [ "$1" != -- ]; do +case $1 in + -h) + usage 0;; + -v) + VERBOSE=1;; + -c) + VARIABLE=$2 + shift;; + -p) + PARSE=1;; + -x) + EXIT=1;; + *) + usage 1;; +esac +shift +done +shift + +test -z "$VARIABLE" && test $# -lt 2 && usage 1 + +# -- get parameters from command line +n_targets=`expr $# - 1` +target="`echo $* | cut -d' ' -f$#`" +source="`echo $* | cut -d' ' -f1-$n_targets`" +#echo target=$target +#echo source=$source + +# -- check run +for s in $source; do + if [ ! -d "$s" -a ! -f "$s" -a ! -L "$s" ]; then + fatal \"$s\" is neither a regular file, nor a directory, nor a link. Exiting. >&2 + fi +done + +eval `parse_target $target` +if [ "$PARSE_TARGET" != OK ]; then + echo "Failed to parse target \"$t\"; exiting." + parse_target $t + exit 2 +fi +case $method in +rsync_ssh) + if [ "$login" != root ]; then + if [ "$file_owner" -a "$file_owner" != "$login" ]; then + fatal "File owner \"$file_owner\" is only legal, if you log in as \"$login\" or as \"root\"." + fi + fi + ;; +"") + ;; +*) + fatal "Support for method \"$method\" is not implemented." >&2 + ;; +esac + +# -- real run + +SSH=ssh +[ "$RSYNC_RSH" ] && SSH=$RSYNC_RSH + +if [ -n "$VARIABLE" ]; then + eval `parse_target` + case "$VARIABLE" in + login) echo $login;; + method) echo $method;; + passwd) echo $passwd;; + file_owner) echo $file_owner;; + file_group) echo $file_group;; + file_mode) echo $file_mode;; + dir_mode) echo $dir_mode;; + source) echo $source;; + target_file) echo $target_file;; + *) fatal Unknown variable \"$VARIABLE\". Exiting. >&2;; + esac + test "$EXIT" = 1 && exit 0 +fi + +if [ "$PARSE" = 1 ]; then + + eval `parse_target` + + for var in login method passwd file_owner file_group file_mode dir_mode source target_file ; do + + eval echo $var = \$$var + done + test "$EXIT" = 1 && exit 0 +fi + +eval `parse_target $target` +case $method in +rsync_ssh) + tmp_dir=/tmp/$login/rsync-ssh + ssh_mkdir $target_file $file_owner $file_group $dir_mode || fatal "failed to create directory for $target_file" + /usr/bin/rsync -az --links $RSYNC_EXTRA_OPTS -e "$SSH -l $login $IDENTITY" $source $login@$host:$target_file + ssh_chown $target_path $file_owner $file_group + ssh_chmod $target_path $file_mode $dir_mode + ;; +"") + #[ "$file_owner" ] || file_owner=`stat -c %u $source | head -1` + #[ "$file_group" ] || file_group=`stat -c %g $source | head -1` + #[ "$file_mode" ] || file_mode=`stat -c %a $source | head -1` + #install -m $file_mode -o $file_owner -g $file_group $source $target + $do_sudo cp $source $target + if [ "$file_owner" ] ; then $do_sudo chown $file_owner $target; fi + if [ "$file_group" ] ; then $do_sudo chgrp $file_group $target; fi + if [ "$file_mode" ] ; then $do_sudo chmod $file_mode $target; fi + ;; +*) + fatal "Internal error: \"$method\" is not implemented." >&2 + ;; +esac +