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