openpkg/lsync

changeset 13
cb59d6afeb61
child 428
f880f219c566
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/openpkg/lsync	Tue Jan 06 23:40:39 2009 +0100
     1.3 @@ -0,0 +1,339 @@
     1.4 +#!@l_prefix@/lib/openpkg/bash
     1.5 +##
     1.6 +##  lsync -- Access Layer Synchronization Tool
     1.7 +##  Copyright (c) 2000-2007 OpenPKG Foundation e.V. <http://openpkg.net/>
     1.8 +##  Copyright (c) 2000-2007 Ralf S. Engelschall <http://engelschall.com/>
     1.9 +##
    1.10 +##  Permission to use, copy, modify, and distribute this software for
    1.11 +##  any purpose with or without fee is hereby granted, provided that
    1.12 +##  the above copyright notice and this permission notice appear in all
    1.13 +##  copies.
    1.14 +##
    1.15 +##  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
    1.16 +##  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    1.17 +##  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    1.18 +##  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
    1.19 +##  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.20 +##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    1.21 +##  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    1.22 +##  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    1.23 +##  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    1.24 +##  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    1.25 +##  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    1.26 +##  SUCH DAMAGE.
    1.27 +##
    1.28 +
    1.29 +##
    1.30 +##  filesystem hierarchy configuration
    1.31 +##
    1.32 +
    1.33 +#   program name and version/date
    1.34 +progname="lsync"
    1.35 +progvers="1.0.4"
    1.36 +progdate="04-Aug-2001"
    1.37 +
    1.38 +#   root directory
    1.39 +#   (if empty, .lsyncrc files provide default)
    1.40 +root=""
    1.41 +
    1.42 +#   subdirectory where packages are physically installed
    1.43 +pkgdir="PKG"
    1.44 +
    1.45 +#   subdirectories which are synchronized between physically
    1.46 +#   installed package areas and access layer
    1.47 +subdirs="bin,sbin,man,info,include,lib"
    1.48 +
    1.49 +##
    1.50 +##  command line option parsing
    1.51 +##
    1.52 +
    1.53 +#   default run-time modes
    1.54 +nop=0
    1.55 +quiet=0
    1.56 +trace=0
    1.57 +help=''
    1.58 +init=0
    1.59 +uninstall=0
    1.60 +local=0
    1.61 +
    1.62 +#   be aware of .lsyncrc files
    1.63 +cwd=`pwd`
    1.64 +while [ 1 ]; do
    1.65 +    if [ -f "$cwd/.lsyncrc" ]; then
    1.66 +        set -- `cat $cwd/.lsyncrc` "$@"
    1.67 +    fi
    1.68 +    [ ".$cwd" = ./ ] && break
    1.69 +    cwd=`echo $cwd | sed -e 's;/[^/]*$;;' -e 's;^$;/;'`
    1.70 +done
    1.71 +if [ ".$HOME" != . -a -f "$HOME/.lsyncrc" ]; then
    1.72 +    set -- `cat $HOME/.lsyncrc` "$@"
    1.73 +fi
    1.74 +
    1.75 +#   iterate over argument line
    1.76 +for opt
    1.77 +do
    1.78 +    case $opt in
    1.79 +        -*=*) arg=`echo "$opt" | sed 's/^[-_a-zA-Z0-9]*=//'` ;;
    1.80 +           *) arg='' ;;
    1.81 +    esac
    1.82 +    case $opt in
    1.83 +        -n|--nop       ) nop=1         ;;
    1.84 +        -q|--quiet     ) quiet=1       ;;
    1.85 +        -t|--trace     ) trace=1       ;;
    1.86 +        -v|--version   ) version=1     ;;
    1.87 +        -h|--help      ) help="Usage"  ;;
    1.88 +        -i|--init      ) init=1        ;;
    1.89 +        -u|--uninstall ) uninstall=1   ;;
    1.90 +        -l|--local     ) local=1       ;;
    1.91 +        --root=*       ) root=$arg     ;;
    1.92 +        --pkgdir=*     ) pkgdir=$arg   ;;
    1.93 +        --subdirs=*    ) subdirs=$arg  ;;
    1.94 +        *              ) help="Invalid option \`$opt'"; break ;;
    1.95 +    esac
    1.96 +done
    1.97 +
    1.98 +#   error or usage message
    1.99 +if [ ".$help" != . ]; then
   1.100 +    if [ ".$help" != ".Usage" ]; then
   1.101 +        echo "$progname:ERROR: $help" 1>&2
   1.102 +    fi
   1.103 +    cat 1>&2 <<EOT
   1.104 +Usage: $progname [options]
   1.105 +
   1.106 +Global options:
   1.107 +  --version,   -v   display tool version information
   1.108 +  --help,      -h   display usage information
   1.109 +  --init,      -i   create an initial directory hierarchy
   1.110 +
   1.111 +Run-time options:
   1.112 +  --nop,       -n   perform no filesystem operations
   1.113 +  --quiet,     -q   display no verbose messages
   1.114 +  --trace,     -t   display performed filesystem operations
   1.115 +  --local,     -l   process a local package area only
   1.116 +  --uninstall, -u   uninstall all files
   1.117 +
   1.118 +Filesystem options:
   1.119 +  --root=DIR        override root directory
   1.120 +  --pkgdir=DIR      override package sub-directory
   1.121 +  --subdirs=DIR     override synchronized sub-directories
   1.122 +
   1.123 +Current configuration:
   1.124 +  root directory:       $root
   1.125 +  package root subdir:  $pkgdir
   1.126 +  synchronized subdirs: $subdirs
   1.127 +EOT
   1.128 +    if [ ".$help" != ".Usage" ]; then
   1.129 +        exit 2
   1.130 +    else
   1.131 +        exit 0
   1.132 +    fi
   1.133 +fi
   1.134 +
   1.135 +#   version information
   1.136 +if [ ".$version" = .1 ]; then
   1.137 +    echo "$progname $progvers ($progdate)"
   1.138 +    exit 0
   1.139 +fi
   1.140 +
   1.141 +#   make sure a root directory was found or specified
   1.142 +if [ ".$root" = . ]; then
   1.143 +    echo "$progname:ERROR: no root directory specified!" 1>&2
   1.144 +    echo "$progname:HINT: use --root=DIR option explicitly on command line" 1>&2
   1.145 +    echo "$progname:HINT: or implicitly inside an .lsyncrc file in your home" 1>&2
   1.146 +    echo "$progname:HINT: directory or in any parent directory." 1>&2
   1.147 +    exit 3
   1.148 +fi
   1.149 +
   1.150 +##
   1.151 +##  helper functions
   1.152 +##
   1.153 +
   1.154 +display_hd () {
   1.155 +    if [ ".$headline" != . ]; then
   1.156 +        if [ ".$quiet" = .0 ]; then
   1.157 +            echo "$headline"
   1.158 +        fi
   1.159 +        headline=''
   1.160 +    fi
   1.161 +}
   1.162 +
   1.163 +display_op () {
   1.164 +    if [ ".$quiet" = .0 ]; then
   1.165 +        echo "  $@"
   1.166 +    fi
   1.167 +}
   1.168 +
   1.169 +display_warning () {
   1.170 +    echo "$progname:WARNING: $*" 1>&2
   1.171 +}
   1.172 +
   1.173 +display_error () {
   1.174 +    echo "$progname:ERROR: $*" 1>&2
   1.175 +}
   1.176 +
   1.177 +perform_op () {
   1.178 +    if [ ".$trace" = .1 ]; then
   1.179 +        echo "  \$ $@"
   1.180 +    fi
   1.181 +    if [ ".$nop" = .0 ]; then
   1.182 +        eval "$@"
   1.183 +    fi
   1.184 +}
   1.185 +
   1.186 +##
   1.187 +##  main processing
   1.188 +##
   1.189 +
   1.190 +#   extend a "man" subdir to a complete list with subdirs
   1.191 +#   in order to avoid special cases in the loop processing
   1.192 +manex=''
   1.193 +if [ ".$init" = .1 ]; then
   1.194 +    manex='man'
   1.195 +fi
   1.196 +for i in 1 2 3 4 5 6 7 8; do
   1.197 +    manex="$manex,man/man$i"
   1.198 +done
   1.199 +manex=`echo $manex | sed -e 's;^,;;'`
   1.200 +subdirs=`echo $subdirs | sed -e "s;man;$manex;"`
   1.201 +
   1.202 +#   special processing: create initial hierarchy
   1.203 +if [ ".$init" = .1 ]; then
   1.204 +    if [ ! -d $root ]; then
   1.205 +        echo "creating $root"
   1.206 +        perform_op "mkdir $root" || exit 1
   1.207 +    fi
   1.208 +    for subdir in $pkgdir `IFS=,; echo $subdirs`; do
   1.209 +        if [ ! -d "$root/$subdir" ]; then
   1.210 +            echo "creating $root/$subdir"
   1.211 +            perform_op "mkdir $root/$subdir" || exit 1
   1.212 +        fi
   1.213 +    done
   1.214 +    exit 0
   1.215 +fi
   1.216 +
   1.217 +#   make sure the root directory actually exists
   1.218 +if [ ! -d "$root" ]; then
   1.219 +    display_warning "root directory \`$root' does not exist"
   1.220 +    exit 3
   1.221 +fi
   1.222 +
   1.223 +#   if processing is restricted to a local package area, pre-determine its name
   1.224 +if [ ".$local" = .1 ]; then
   1.225 +   realroot=`cd $root; pwd`
   1.226 +   realthis=`pwd`
   1.227 +   pkgname=`expr "$realthis" : "^$realroot/$pkgdir/\\([^/]*\\).*"`
   1.228 +   if [ ".$pkgname" = . ]; then
   1.229 +       display_error "you are not staying under a local package sub-directory"
   1.230 +       exit 3
   1.231 +   fi
   1.232 +fi
   1.233 +
   1.234 +#   now perform the synchronization for each sub-directory...
   1.235 +for subdir in `IFS=,; echo $subdirs`; do
   1.236 +    headline="$root/$subdir:"
   1.237 +
   1.238 +    #   make sure the subdir actually exists in the access layer
   1.239 +    if [ ! -d "$root/$subdir" ]; then
   1.240 +        display_warning "access layer directory \`$root/$subdir' does not exist"
   1.241 +        continue
   1.242 +    fi
   1.243 +
   1.244 +    #
   1.245 +    #   PASS 1: remove dangling symbolic links in access layer
   1.246 +    #
   1.247 +
   1.248 +    #   iterate over all symlinks in the access layer subdir
   1.249 +    for link in . `ls "$root/$subdir/" | sed -e "s;^$root/$subdir/*;;g"`; do
   1.250 +        test ".$link" = ".." && continue
   1.251 +
   1.252 +        #   determine the target file of the symlink
   1.253 +        target=`ls -l "$root/$subdir/$link" 2>/dev/null | sed -e 's;.*-> *;;'`
   1.254 +        if [ ".$target" = . ]; then
   1.255 +            display_warning "$root/$subdir/$link seems to be not a symbolic link"
   1.256 +            continue
   1.257 +        fi
   1.258 +
   1.259 +        #   (optionally) make sure that link target points into local package area
   1.260 +        if [ ".$local" = .1 -a .`expr $target : "../$pkgdir/$pkgname/.*"` = .0 ]; then
   1.261 +            continue
   1.262 +        fi
   1.263 +
   1.264 +        #   check whether link is valid, i.e., points to
   1.265 +        #   an existing target file or directory
   1.266 +        if [ ".$uninstall" = .1 ] ||\
   1.267 +           [ ! -f "$root/$subdir/$target" -a \
   1.268 +             ! -d "$root/$subdir/$target"      ]; then
   1.269 +            #   target no longer exists, so remove dangling symlink
   1.270 +            display_hd
   1.271 +            display_op "remove: $link -> $target"
   1.272 +            perform_op "rm -f $root/$subdir/$link"
   1.273 +        fi
   1.274 +    done
   1.275 +
   1.276 +    #   if we are uninstalling only, our work is now done
   1.277 +    if [ ".$uninstall" = ".1" ]; then
   1.278 +        continue
   1.279 +    fi
   1.280 +
   1.281 +    #
   1.282 +    #   PASS 2: create new symbolic links in access layer
   1.283 +    #
   1.284 +
   1.285 +    #   calculate the corresponding reverse directory for the current subdir
   1.286 +    revdir=`echo $subdir | sed -e 's;[^/][^/]*;..;g'`
   1.287 +
   1.288 +    #   iterate over all package directories
   1.289 +    for dir in . `ls "$root/$pkgdir/" | sed -e "s;^$root/$pkgdir/*;;g"`; do
   1.290 +        test ".$dir" = ".." && continue
   1.291 +
   1.292 +        #   (optionally) make sure that we operate only for the local package area
   1.293 +        if [ ".$local" = .1 -a ".$dir" != ".$pkgname" ]; then
   1.294 +            continue
   1.295 +        fi
   1.296 +
   1.297 +        #   skip all directories with appended version numbers
   1.298 +        #   in order to support manual versioning of packages
   1.299 +        case $dir in
   1.300 +            *-[0-9]* ) continue ;;
   1.301 +        esac
   1.302 +
   1.303 +        #   skip if package directory or package sub-directories has sticky bit set
   1.304 +        if [ ".`ls -l -d $root/$pkgdir/$dir 2>/dev/null | cut -c10`" = .t ] ||\
   1.305 +           [ ".`ls -l -d $root/$pkgdir/$dir/$subdir 2>/dev/null | cut -c10`" = .t ]; then
   1.306 +            continue
   1.307 +        fi
   1.308 +
   1.309 +        #   check whether the processed subdir exists in package area
   1.310 +        if [ -d "$root/$pkgdir/$dir/$subdir" ]; then
   1.311 +
   1.312 +            #   iterate over all files/directories in package's subdir
   1.313 +            for file in . `ls "$root/$pkgdir/$dir/$subdir/" |\
   1.314 +                           sed -e "s;^$root/$pkgdir/$dir/$subdir/*;;g"`; do
   1.315 +                test ".$file" = ".." && continue
   1.316 +
   1.317 +                #   calculate the access layer symlink target
   1.318 +                target="$revdir/$pkgdir/$dir/$subdir/$file"
   1.319 +
   1.320 +                #   check whether a possibly conflicting symlink exists
   1.321 +                exlink=`ls -l $root/$subdir/$file 2>/dev/null`
   1.322 +                if [ ".$exlink" != . ]; then
   1.323 +                    extarget=`echo $exlink | sed -e 's;.*-> *;;'`
   1.324 +                    if [ ".$extarget" = . ]; then
   1.325 +                        display_warning "$root/$subdir/$file exits, but seems to be not a symbolic link"
   1.326 +                    elif [ ".$extarget" != ".$target" ]; then
   1.327 +                        display_hd
   1.328 +                        display_op "conflict: $file -> $extarget [existing]"
   1.329 +                        display_op "          $file -> $target [alternative]"
   1.330 +                    fi
   1.331 +                    continue
   1.332 +                fi
   1.333 +
   1.334 +                #   create new symlink in access layer
   1.335 +                display_hd
   1.336 +                display_op "create: $file -> $target"
   1.337 +                perform_op "cd $root/$subdir && ln -s $target $file"
   1.338 +            done
   1.339 +        fi
   1.340 +    done
   1.341 +done
   1.342 +

mercurial