Mon, 28 Jan 2013 17:37:18 +0100
Correct socket error reporting improvement with IPv6 portable code,
after helpful recommendation by Saúl Ibarra Corretgé on OSips devlist.
michael@13 | 1 | #!@l_prefix@/lib/openpkg/bash |
michael@13 | 2 | ## |
michael@13 | 3 | ## lsync -- Access Layer Synchronization Tool |
michael@428 | 4 | ## Copyright (c) 2000-2012 OpenPKG GmbH <http://openpkg.com/> |
michael@13 | 5 | ## |
michael@428 | 6 | ## This software is property of the OpenPKG GmbH, DE MUC HRB 160208. |
michael@428 | 7 | ## All rights reserved. Licenses which grant limited permission to use, |
michael@428 | 8 | ## copy, modify and distribute this software are available from the |
michael@428 | 9 | ## OpenPKG GmbH. |
michael@13 | 10 | ## |
michael@428 | 11 | ## THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED |
michael@13 | 12 | ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
michael@13 | 13 | ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
michael@13 | 14 | ## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR |
michael@13 | 15 | ## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
michael@13 | 16 | ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
michael@13 | 17 | ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
michael@13 | 18 | ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
michael@13 | 19 | ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
michael@13 | 20 | ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
michael@13 | 21 | ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
michael@13 | 22 | ## SUCH DAMAGE. |
michael@13 | 23 | ## |
michael@13 | 24 | |
michael@13 | 25 | ## |
michael@13 | 26 | ## filesystem hierarchy configuration |
michael@13 | 27 | ## |
michael@13 | 28 | |
michael@13 | 29 | # program name and version/date |
michael@13 | 30 | progname="lsync" |
michael@13 | 31 | progvers="1.0.4" |
michael@13 | 32 | progdate="04-Aug-2001" |
michael@13 | 33 | |
michael@13 | 34 | # root directory |
michael@13 | 35 | # (if empty, .lsyncrc files provide default) |
michael@13 | 36 | root="" |
michael@13 | 37 | |
michael@13 | 38 | # subdirectory where packages are physically installed |
michael@13 | 39 | pkgdir="PKG" |
michael@13 | 40 | |
michael@13 | 41 | # subdirectories which are synchronized between physically |
michael@13 | 42 | # installed package areas and access layer |
michael@13 | 43 | subdirs="bin,sbin,man,info,include,lib" |
michael@13 | 44 | |
michael@13 | 45 | ## |
michael@13 | 46 | ## command line option parsing |
michael@13 | 47 | ## |
michael@13 | 48 | |
michael@13 | 49 | # default run-time modes |
michael@13 | 50 | nop=0 |
michael@13 | 51 | quiet=0 |
michael@13 | 52 | trace=0 |
michael@13 | 53 | help='' |
michael@13 | 54 | init=0 |
michael@13 | 55 | uninstall=0 |
michael@13 | 56 | local=0 |
michael@13 | 57 | |
michael@13 | 58 | # be aware of .lsyncrc files |
michael@13 | 59 | cwd=`pwd` |
michael@13 | 60 | while [ 1 ]; do |
michael@13 | 61 | if [ -f "$cwd/.lsyncrc" ]; then |
michael@13 | 62 | set -- `cat $cwd/.lsyncrc` "$@" |
michael@13 | 63 | fi |
michael@13 | 64 | [ ".$cwd" = ./ ] && break |
michael@13 | 65 | cwd=`echo $cwd | sed -e 's;/[^/]*$;;' -e 's;^$;/;'` |
michael@13 | 66 | done |
michael@13 | 67 | if [ ".$HOME" != . -a -f "$HOME/.lsyncrc" ]; then |
michael@13 | 68 | set -- `cat $HOME/.lsyncrc` "$@" |
michael@13 | 69 | fi |
michael@13 | 70 | |
michael@13 | 71 | # iterate over argument line |
michael@13 | 72 | for opt |
michael@13 | 73 | do |
michael@13 | 74 | case $opt in |
michael@13 | 75 | -*=*) arg=`echo "$opt" | sed 's/^[-_a-zA-Z0-9]*=//'` ;; |
michael@13 | 76 | *) arg='' ;; |
michael@13 | 77 | esac |
michael@13 | 78 | case $opt in |
michael@13 | 79 | -n|--nop ) nop=1 ;; |
michael@13 | 80 | -q|--quiet ) quiet=1 ;; |
michael@13 | 81 | -t|--trace ) trace=1 ;; |
michael@13 | 82 | -v|--version ) version=1 ;; |
michael@13 | 83 | -h|--help ) help="Usage" ;; |
michael@13 | 84 | -i|--init ) init=1 ;; |
michael@13 | 85 | -u|--uninstall ) uninstall=1 ;; |
michael@13 | 86 | -l|--local ) local=1 ;; |
michael@13 | 87 | --root=* ) root=$arg ;; |
michael@13 | 88 | --pkgdir=* ) pkgdir=$arg ;; |
michael@13 | 89 | --subdirs=* ) subdirs=$arg ;; |
michael@13 | 90 | * ) help="Invalid option \`$opt'"; break ;; |
michael@13 | 91 | esac |
michael@13 | 92 | done |
michael@13 | 93 | |
michael@13 | 94 | # error or usage message |
michael@13 | 95 | if [ ".$help" != . ]; then |
michael@13 | 96 | if [ ".$help" != ".Usage" ]; then |
michael@13 | 97 | echo "$progname:ERROR: $help" 1>&2 |
michael@13 | 98 | fi |
michael@13 | 99 | cat 1>&2 <<EOT |
michael@13 | 100 | Usage: $progname [options] |
michael@13 | 101 | |
michael@13 | 102 | Global options: |
michael@13 | 103 | --version, -v display tool version information |
michael@13 | 104 | --help, -h display usage information |
michael@13 | 105 | --init, -i create an initial directory hierarchy |
michael@13 | 106 | |
michael@13 | 107 | Run-time options: |
michael@13 | 108 | --nop, -n perform no filesystem operations |
michael@13 | 109 | --quiet, -q display no verbose messages |
michael@13 | 110 | --trace, -t display performed filesystem operations |
michael@13 | 111 | --local, -l process a local package area only |
michael@13 | 112 | --uninstall, -u uninstall all files |
michael@13 | 113 | |
michael@13 | 114 | Filesystem options: |
michael@13 | 115 | --root=DIR override root directory |
michael@13 | 116 | --pkgdir=DIR override package sub-directory |
michael@13 | 117 | --subdirs=DIR override synchronized sub-directories |
michael@13 | 118 | |
michael@13 | 119 | Current configuration: |
michael@13 | 120 | root directory: $root |
michael@13 | 121 | package root subdir: $pkgdir |
michael@13 | 122 | synchronized subdirs: $subdirs |
michael@13 | 123 | EOT |
michael@13 | 124 | if [ ".$help" != ".Usage" ]; then |
michael@13 | 125 | exit 2 |
michael@13 | 126 | else |
michael@13 | 127 | exit 0 |
michael@13 | 128 | fi |
michael@13 | 129 | fi |
michael@13 | 130 | |
michael@13 | 131 | # version information |
michael@13 | 132 | if [ ".$version" = .1 ]; then |
michael@13 | 133 | echo "$progname $progvers ($progdate)" |
michael@13 | 134 | exit 0 |
michael@13 | 135 | fi |
michael@13 | 136 | |
michael@13 | 137 | # make sure a root directory was found or specified |
michael@13 | 138 | if [ ".$root" = . ]; then |
michael@13 | 139 | echo "$progname:ERROR: no root directory specified!" 1>&2 |
michael@13 | 140 | echo "$progname:HINT: use --root=DIR option explicitly on command line" 1>&2 |
michael@13 | 141 | echo "$progname:HINT: or implicitly inside an .lsyncrc file in your home" 1>&2 |
michael@13 | 142 | echo "$progname:HINT: directory or in any parent directory." 1>&2 |
michael@13 | 143 | exit 3 |
michael@13 | 144 | fi |
michael@13 | 145 | |
michael@13 | 146 | ## |
michael@13 | 147 | ## helper functions |
michael@13 | 148 | ## |
michael@13 | 149 | |
michael@13 | 150 | display_hd () { |
michael@13 | 151 | if [ ".$headline" != . ]; then |
michael@13 | 152 | if [ ".$quiet" = .0 ]; then |
michael@13 | 153 | echo "$headline" |
michael@13 | 154 | fi |
michael@13 | 155 | headline='' |
michael@13 | 156 | fi |
michael@13 | 157 | } |
michael@13 | 158 | |
michael@13 | 159 | display_op () { |
michael@13 | 160 | if [ ".$quiet" = .0 ]; then |
michael@13 | 161 | echo " $@" |
michael@13 | 162 | fi |
michael@13 | 163 | } |
michael@13 | 164 | |
michael@13 | 165 | display_warning () { |
michael@13 | 166 | echo "$progname:WARNING: $*" 1>&2 |
michael@13 | 167 | } |
michael@13 | 168 | |
michael@13 | 169 | display_error () { |
michael@13 | 170 | echo "$progname:ERROR: $*" 1>&2 |
michael@13 | 171 | } |
michael@13 | 172 | |
michael@13 | 173 | perform_op () { |
michael@13 | 174 | if [ ".$trace" = .1 ]; then |
michael@13 | 175 | echo " \$ $@" |
michael@13 | 176 | fi |
michael@13 | 177 | if [ ".$nop" = .0 ]; then |
michael@13 | 178 | eval "$@" |
michael@13 | 179 | fi |
michael@13 | 180 | } |
michael@13 | 181 | |
michael@13 | 182 | ## |
michael@13 | 183 | ## main processing |
michael@13 | 184 | ## |
michael@13 | 185 | |
michael@13 | 186 | # extend a "man" subdir to a complete list with subdirs |
michael@13 | 187 | # in order to avoid special cases in the loop processing |
michael@13 | 188 | manex='' |
michael@13 | 189 | if [ ".$init" = .1 ]; then |
michael@13 | 190 | manex='man' |
michael@13 | 191 | fi |
michael@13 | 192 | for i in 1 2 3 4 5 6 7 8; do |
michael@13 | 193 | manex="$manex,man/man$i" |
michael@13 | 194 | done |
michael@13 | 195 | manex=`echo $manex | sed -e 's;^,;;'` |
michael@13 | 196 | subdirs=`echo $subdirs | sed -e "s;man;$manex;"` |
michael@13 | 197 | |
michael@13 | 198 | # special processing: create initial hierarchy |
michael@13 | 199 | if [ ".$init" = .1 ]; then |
michael@13 | 200 | if [ ! -d $root ]; then |
michael@13 | 201 | echo "creating $root" |
michael@13 | 202 | perform_op "mkdir $root" || exit 1 |
michael@13 | 203 | fi |
michael@13 | 204 | for subdir in $pkgdir `IFS=,; echo $subdirs`; do |
michael@13 | 205 | if [ ! -d "$root/$subdir" ]; then |
michael@13 | 206 | echo "creating $root/$subdir" |
michael@13 | 207 | perform_op "mkdir $root/$subdir" || exit 1 |
michael@13 | 208 | fi |
michael@13 | 209 | done |
michael@13 | 210 | exit 0 |
michael@13 | 211 | fi |
michael@13 | 212 | |
michael@13 | 213 | # make sure the root directory actually exists |
michael@13 | 214 | if [ ! -d "$root" ]; then |
michael@13 | 215 | display_warning "root directory \`$root' does not exist" |
michael@13 | 216 | exit 3 |
michael@13 | 217 | fi |
michael@13 | 218 | |
michael@13 | 219 | # if processing is restricted to a local package area, pre-determine its name |
michael@13 | 220 | if [ ".$local" = .1 ]; then |
michael@13 | 221 | realroot=`cd $root; pwd` |
michael@13 | 222 | realthis=`pwd` |
michael@13 | 223 | pkgname=`expr "$realthis" : "^$realroot/$pkgdir/\\([^/]*\\).*"` |
michael@13 | 224 | if [ ".$pkgname" = . ]; then |
michael@13 | 225 | display_error "you are not staying under a local package sub-directory" |
michael@13 | 226 | exit 3 |
michael@13 | 227 | fi |
michael@13 | 228 | fi |
michael@13 | 229 | |
michael@13 | 230 | # now perform the synchronization for each sub-directory... |
michael@13 | 231 | for subdir in `IFS=,; echo $subdirs`; do |
michael@13 | 232 | headline="$root/$subdir:" |
michael@13 | 233 | |
michael@13 | 234 | # make sure the subdir actually exists in the access layer |
michael@13 | 235 | if [ ! -d "$root/$subdir" ]; then |
michael@13 | 236 | display_warning "access layer directory \`$root/$subdir' does not exist" |
michael@13 | 237 | continue |
michael@13 | 238 | fi |
michael@13 | 239 | |
michael@13 | 240 | # |
michael@13 | 241 | # PASS 1: remove dangling symbolic links in access layer |
michael@13 | 242 | # |
michael@13 | 243 | |
michael@13 | 244 | # iterate over all symlinks in the access layer subdir |
michael@13 | 245 | for link in . `ls "$root/$subdir/" | sed -e "s;^$root/$subdir/*;;g"`; do |
michael@13 | 246 | test ".$link" = ".." && continue |
michael@13 | 247 | |
michael@13 | 248 | # determine the target file of the symlink |
michael@13 | 249 | target=`ls -l "$root/$subdir/$link" 2>/dev/null | sed -e 's;.*-> *;;'` |
michael@13 | 250 | if [ ".$target" = . ]; then |
michael@13 | 251 | display_warning "$root/$subdir/$link seems to be not a symbolic link" |
michael@13 | 252 | continue |
michael@13 | 253 | fi |
michael@13 | 254 | |
michael@13 | 255 | # (optionally) make sure that link target points into local package area |
michael@13 | 256 | if [ ".$local" = .1 -a .`expr $target : "../$pkgdir/$pkgname/.*"` = .0 ]; then |
michael@13 | 257 | continue |
michael@13 | 258 | fi |
michael@13 | 259 | |
michael@13 | 260 | # check whether link is valid, i.e., points to |
michael@13 | 261 | # an existing target file or directory |
michael@13 | 262 | if [ ".$uninstall" = .1 ] ||\ |
michael@13 | 263 | [ ! -f "$root/$subdir/$target" -a \ |
michael@13 | 264 | ! -d "$root/$subdir/$target" ]; then |
michael@13 | 265 | # target no longer exists, so remove dangling symlink |
michael@13 | 266 | display_hd |
michael@13 | 267 | display_op "remove: $link -> $target" |
michael@13 | 268 | perform_op "rm -f $root/$subdir/$link" |
michael@13 | 269 | fi |
michael@13 | 270 | done |
michael@13 | 271 | |
michael@13 | 272 | # if we are uninstalling only, our work is now done |
michael@13 | 273 | if [ ".$uninstall" = ".1" ]; then |
michael@13 | 274 | continue |
michael@13 | 275 | fi |
michael@13 | 276 | |
michael@13 | 277 | # |
michael@13 | 278 | # PASS 2: create new symbolic links in access layer |
michael@13 | 279 | # |
michael@13 | 280 | |
michael@13 | 281 | # calculate the corresponding reverse directory for the current subdir |
michael@13 | 282 | revdir=`echo $subdir | sed -e 's;[^/][^/]*;..;g'` |
michael@13 | 283 | |
michael@13 | 284 | # iterate over all package directories |
michael@13 | 285 | for dir in . `ls "$root/$pkgdir/" | sed -e "s;^$root/$pkgdir/*;;g"`; do |
michael@13 | 286 | test ".$dir" = ".." && continue |
michael@13 | 287 | |
michael@13 | 288 | # (optionally) make sure that we operate only for the local package area |
michael@13 | 289 | if [ ".$local" = .1 -a ".$dir" != ".$pkgname" ]; then |
michael@13 | 290 | continue |
michael@13 | 291 | fi |
michael@13 | 292 | |
michael@13 | 293 | # skip all directories with appended version numbers |
michael@13 | 294 | # in order to support manual versioning of packages |
michael@13 | 295 | case $dir in |
michael@13 | 296 | *-[0-9]* ) continue ;; |
michael@13 | 297 | esac |
michael@13 | 298 | |
michael@13 | 299 | # skip if package directory or package sub-directories has sticky bit set |
michael@13 | 300 | if [ ".`ls -l -d $root/$pkgdir/$dir 2>/dev/null | cut -c10`" = .t ] ||\ |
michael@13 | 301 | [ ".`ls -l -d $root/$pkgdir/$dir/$subdir 2>/dev/null | cut -c10`" = .t ]; then |
michael@13 | 302 | continue |
michael@13 | 303 | fi |
michael@13 | 304 | |
michael@13 | 305 | # check whether the processed subdir exists in package area |
michael@13 | 306 | if [ -d "$root/$pkgdir/$dir/$subdir" ]; then |
michael@13 | 307 | |
michael@13 | 308 | # iterate over all files/directories in package's subdir |
michael@13 | 309 | for file in . `ls "$root/$pkgdir/$dir/$subdir/" |\ |
michael@13 | 310 | sed -e "s;^$root/$pkgdir/$dir/$subdir/*;;g"`; do |
michael@13 | 311 | test ".$file" = ".." && continue |
michael@13 | 312 | |
michael@13 | 313 | # calculate the access layer symlink target |
michael@13 | 314 | target="$revdir/$pkgdir/$dir/$subdir/$file" |
michael@13 | 315 | |
michael@13 | 316 | # check whether a possibly conflicting symlink exists |
michael@13 | 317 | exlink=`ls -l $root/$subdir/$file 2>/dev/null` |
michael@13 | 318 | if [ ".$exlink" != . ]; then |
michael@13 | 319 | extarget=`echo $exlink | sed -e 's;.*-> *;;'` |
michael@13 | 320 | if [ ".$extarget" = . ]; then |
michael@13 | 321 | display_warning "$root/$subdir/$file exits, but seems to be not a symbolic link" |
michael@13 | 322 | elif [ ".$extarget" != ".$target" ]; then |
michael@13 | 323 | display_hd |
michael@13 | 324 | display_op "conflict: $file -> $extarget [existing]" |
michael@13 | 325 | display_op " $file -> $target [alternative]" |
michael@13 | 326 | fi |
michael@13 | 327 | continue |
michael@13 | 328 | fi |
michael@13 | 329 | |
michael@13 | 330 | # create new symlink in access layer |
michael@13 | 331 | display_hd |
michael@13 | 332 | display_op "create: $file -> $target" |
michael@13 | 333 | perform_op "cd $root/$subdir && ln -s $target $file" |
michael@13 | 334 | done |
michael@13 | 335 | fi |
michael@13 | 336 | done |
michael@13 | 337 | done |
michael@13 | 338 |