| |
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 |