michael@13: #!@l_prefix@/lib/openpkg/bash michael@13: ## michael@13: ## lsync -- Access Layer Synchronization Tool michael@428: ## Copyright (c) 2000-2012 OpenPKG GmbH michael@13: ## michael@428: ## This software is property of the OpenPKG GmbH, DE MUC HRB 160208. michael@428: ## All rights reserved. Licenses which grant limited permission to use, michael@428: ## copy, modify and distribute this software are available from the michael@428: ## OpenPKG GmbH. michael@13: ## michael@428: ## THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED michael@13: ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF michael@13: ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. michael@13: ## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR michael@13: ## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@13: ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@13: ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF michael@13: ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND michael@13: ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, michael@13: ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT michael@13: ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF michael@13: ## SUCH DAMAGE. michael@13: ## michael@13: michael@13: ## michael@13: ## filesystem hierarchy configuration michael@13: ## michael@13: michael@13: # program name and version/date michael@13: progname="lsync" michael@13: progvers="1.0.4" michael@13: progdate="04-Aug-2001" michael@13: michael@13: # root directory michael@13: # (if empty, .lsyncrc files provide default) michael@13: root="" michael@13: michael@13: # subdirectory where packages are physically installed michael@13: pkgdir="PKG" michael@13: michael@13: # subdirectories which are synchronized between physically michael@13: # installed package areas and access layer michael@13: subdirs="bin,sbin,man,info,include,lib" michael@13: michael@13: ## michael@13: ## command line option parsing michael@13: ## michael@13: michael@13: # default run-time modes michael@13: nop=0 michael@13: quiet=0 michael@13: trace=0 michael@13: help='' michael@13: init=0 michael@13: uninstall=0 michael@13: local=0 michael@13: michael@13: # be aware of .lsyncrc files michael@13: cwd=`pwd` michael@13: while [ 1 ]; do michael@13: if [ -f "$cwd/.lsyncrc" ]; then michael@13: set -- `cat $cwd/.lsyncrc` "$@" michael@13: fi michael@13: [ ".$cwd" = ./ ] && break michael@13: cwd=`echo $cwd | sed -e 's;/[^/]*$;;' -e 's;^$;/;'` michael@13: done michael@13: if [ ".$HOME" != . -a -f "$HOME/.lsyncrc" ]; then michael@13: set -- `cat $HOME/.lsyncrc` "$@" michael@13: fi michael@13: michael@13: # iterate over argument line michael@13: for opt michael@13: do michael@13: case $opt in michael@13: -*=*) arg=`echo "$opt" | sed 's/^[-_a-zA-Z0-9]*=//'` ;; michael@13: *) arg='' ;; michael@13: esac michael@13: case $opt in michael@13: -n|--nop ) nop=1 ;; michael@13: -q|--quiet ) quiet=1 ;; michael@13: -t|--trace ) trace=1 ;; michael@13: -v|--version ) version=1 ;; michael@13: -h|--help ) help="Usage" ;; michael@13: -i|--init ) init=1 ;; michael@13: -u|--uninstall ) uninstall=1 ;; michael@13: -l|--local ) local=1 ;; michael@13: --root=* ) root=$arg ;; michael@13: --pkgdir=* ) pkgdir=$arg ;; michael@13: --subdirs=* ) subdirs=$arg ;; michael@13: * ) help="Invalid option \`$opt'"; break ;; michael@13: esac michael@13: done michael@13: michael@13: # error or usage message michael@13: if [ ".$help" != . ]; then michael@13: if [ ".$help" != ".Usage" ]; then michael@13: echo "$progname:ERROR: $help" 1>&2 michael@13: fi michael@13: cat 1>&2 <&2 michael@13: echo "$progname:HINT: use --root=DIR option explicitly on command line" 1>&2 michael@13: echo "$progname:HINT: or implicitly inside an .lsyncrc file in your home" 1>&2 michael@13: echo "$progname:HINT: directory or in any parent directory." 1>&2 michael@13: exit 3 michael@13: fi michael@13: michael@13: ## michael@13: ## helper functions michael@13: ## michael@13: michael@13: display_hd () { michael@13: if [ ".$headline" != . ]; then michael@13: if [ ".$quiet" = .0 ]; then michael@13: echo "$headline" michael@13: fi michael@13: headline='' michael@13: fi michael@13: } michael@13: michael@13: display_op () { michael@13: if [ ".$quiet" = .0 ]; then michael@13: echo " $@" michael@13: fi michael@13: } michael@13: michael@13: display_warning () { michael@13: echo "$progname:WARNING: $*" 1>&2 michael@13: } michael@13: michael@13: display_error () { michael@13: echo "$progname:ERROR: $*" 1>&2 michael@13: } michael@13: michael@13: perform_op () { michael@13: if [ ".$trace" = .1 ]; then michael@13: echo " \$ $@" michael@13: fi michael@13: if [ ".$nop" = .0 ]; then michael@13: eval "$@" michael@13: fi michael@13: } michael@13: michael@13: ## michael@13: ## main processing michael@13: ## michael@13: michael@13: # extend a "man" subdir to a complete list with subdirs michael@13: # in order to avoid special cases in the loop processing michael@13: manex='' michael@13: if [ ".$init" = .1 ]; then michael@13: manex='man' michael@13: fi michael@13: for i in 1 2 3 4 5 6 7 8; do michael@13: manex="$manex,man/man$i" michael@13: done michael@13: manex=`echo $manex | sed -e 's;^,;;'` michael@13: subdirs=`echo $subdirs | sed -e "s;man;$manex;"` michael@13: michael@13: # special processing: create initial hierarchy michael@13: if [ ".$init" = .1 ]; then michael@13: if [ ! -d $root ]; then michael@13: echo "creating $root" michael@13: perform_op "mkdir $root" || exit 1 michael@13: fi michael@13: for subdir in $pkgdir `IFS=,; echo $subdirs`; do michael@13: if [ ! -d "$root/$subdir" ]; then michael@13: echo "creating $root/$subdir" michael@13: perform_op "mkdir $root/$subdir" || exit 1 michael@13: fi michael@13: done michael@13: exit 0 michael@13: fi michael@13: michael@13: # make sure the root directory actually exists michael@13: if [ ! -d "$root" ]; then michael@13: display_warning "root directory \`$root' does not exist" michael@13: exit 3 michael@13: fi michael@13: michael@13: # if processing is restricted to a local package area, pre-determine its name michael@13: if [ ".$local" = .1 ]; then michael@13: realroot=`cd $root; pwd` michael@13: realthis=`pwd` michael@13: pkgname=`expr "$realthis" : "^$realroot/$pkgdir/\\([^/]*\\).*"` michael@13: if [ ".$pkgname" = . ]; then michael@13: display_error "you are not staying under a local package sub-directory" michael@13: exit 3 michael@13: fi michael@13: fi michael@13: michael@13: # now perform the synchronization for each sub-directory... michael@13: for subdir in `IFS=,; echo $subdirs`; do michael@13: headline="$root/$subdir:" michael@13: michael@13: # make sure the subdir actually exists in the access layer michael@13: if [ ! -d "$root/$subdir" ]; then michael@13: display_warning "access layer directory \`$root/$subdir' does not exist" michael@13: continue michael@13: fi michael@13: michael@13: # michael@13: # PASS 1: remove dangling symbolic links in access layer michael@13: # michael@13: michael@13: # iterate over all symlinks in the access layer subdir michael@13: for link in . `ls "$root/$subdir/" | sed -e "s;^$root/$subdir/*;;g"`; do michael@13: test ".$link" = ".." && continue michael@13: michael@13: # determine the target file of the symlink michael@13: target=`ls -l "$root/$subdir/$link" 2>/dev/null | sed -e 's;.*-> *;;'` michael@13: if [ ".$target" = . ]; then michael@13: display_warning "$root/$subdir/$link seems to be not a symbolic link" michael@13: continue michael@13: fi michael@13: michael@13: # (optionally) make sure that link target points into local package area michael@13: if [ ".$local" = .1 -a .`expr $target : "../$pkgdir/$pkgname/.*"` = .0 ]; then michael@13: continue michael@13: fi michael@13: michael@13: # check whether link is valid, i.e., points to michael@13: # an existing target file or directory michael@13: if [ ".$uninstall" = .1 ] ||\ michael@13: [ ! -f "$root/$subdir/$target" -a \ michael@13: ! -d "$root/$subdir/$target" ]; then michael@13: # target no longer exists, so remove dangling symlink michael@13: display_hd michael@13: display_op "remove: $link -> $target" michael@13: perform_op "rm -f $root/$subdir/$link" michael@13: fi michael@13: done michael@13: michael@13: # if we are uninstalling only, our work is now done michael@13: if [ ".$uninstall" = ".1" ]; then michael@13: continue michael@13: fi michael@13: michael@13: # michael@13: # PASS 2: create new symbolic links in access layer michael@13: # michael@13: michael@13: # calculate the corresponding reverse directory for the current subdir michael@13: revdir=`echo $subdir | sed -e 's;[^/][^/]*;..;g'` michael@13: michael@13: # iterate over all package directories michael@13: for dir in . `ls "$root/$pkgdir/" | sed -e "s;^$root/$pkgdir/*;;g"`; do michael@13: test ".$dir" = ".." && continue michael@13: michael@13: # (optionally) make sure that we operate only for the local package area michael@13: if [ ".$local" = .1 -a ".$dir" != ".$pkgname" ]; then michael@13: continue michael@13: fi michael@13: michael@13: # skip all directories with appended version numbers michael@13: # in order to support manual versioning of packages michael@13: case $dir in michael@13: *-[0-9]* ) continue ;; michael@13: esac michael@13: michael@13: # skip if package directory or package sub-directories has sticky bit set michael@13: if [ ".`ls -l -d $root/$pkgdir/$dir 2>/dev/null | cut -c10`" = .t ] ||\ michael@13: [ ".`ls -l -d $root/$pkgdir/$dir/$subdir 2>/dev/null | cut -c10`" = .t ]; then michael@13: continue michael@13: fi michael@13: michael@13: # check whether the processed subdir exists in package area michael@13: if [ -d "$root/$pkgdir/$dir/$subdir" ]; then michael@13: michael@13: # iterate over all files/directories in package's subdir michael@13: for file in . `ls "$root/$pkgdir/$dir/$subdir/" |\ michael@13: sed -e "s;^$root/$pkgdir/$dir/$subdir/*;;g"`; do michael@13: test ".$file" = ".." && continue michael@13: michael@13: # calculate the access layer symlink target michael@13: target="$revdir/$pkgdir/$dir/$subdir/$file" michael@13: michael@13: # check whether a possibly conflicting symlink exists michael@13: exlink=`ls -l $root/$subdir/$file 2>/dev/null` michael@13: if [ ".$exlink" != . ]; then michael@13: extarget=`echo $exlink | sed -e 's;.*-> *;;'` michael@13: if [ ".$extarget" = . ]; then michael@13: display_warning "$root/$subdir/$file exits, but seems to be not a symbolic link" michael@13: elif [ ".$extarget" != ".$target" ]; then michael@13: display_hd michael@13: display_op "conflict: $file -> $extarget [existing]" michael@13: display_op " $file -> $target [alternative]" michael@13: fi michael@13: continue michael@13: fi michael@13: michael@13: # create new symlink in access layer michael@13: display_hd michael@13: display_op "create: $file -> $target" michael@13: perform_op "cd $root/$subdir && ln -s $target $file" michael@13: done michael@13: fi michael@13: done michael@13: done michael@13: