/[polypkg]/trunk/pp.back.aix
ViewVC logotype

Contents of /trunk/pp.back.aix

Parent Directory Parent Directory | Revision Log Revision Log


Revision 212 - (show annotations)
Fri Feb 13 23:09:36 2009 UTC (9 months, 1 week ago) by tperciva
File size: 19530 byte(s)
aix: truncate 5-component versions to 4 components

5-component versions are only valid for partial filesets, not
base filesets (full packages). We only generate base filesets.

An overlong version number will now yield a warning & truncation, rather
than an error.
1 #
2 # AIX backend
3 # http://publib.boulder.ibm.com/infocenter/pseries/v5r3/topic/com.ibm.aix.genprogc/doc/genprogc/pkging_sw4_install.htm
4 # When debugging, set the environment variable INST_DEBUG=DEBUG for installp.
5
6 # There is no ldd on AIX; use 'dump -H' instead
7
8 pp_platforms="$pp_platforms aix"
9
10 #@ pp_backend_aix_detect(): return true if current platform does AIX packaging
11 pp_backend_aix_detect () {
12 test x"$1" = x"AIX"
13 }
14
15 #@ pp_backend_aix_init(): initialise the
16 pp_backend_aix_init () {
17 pp_aix_detect_arch
18 pp_aix_detect_os
19
20 pp_aix_bosboot= # components that need bosboot
21 pp_aix_lang=en_US
22 pp_aix_copyright="$pp_copyright"
23 pp_aix_start_services_after_install=false
24 pp_aix_init_services_after_install=true
25
26 case "$pp_aix_os" in
27 *) pp_readlink_fn=pp_ls_readlink;; # XXX
28 esac
29
30 pp_aix_abis_seen=
31 }
32
33 pp_aix_detect_arch () {
34 pp_aix_arch_p=`uname -p 2>/dev/null`
35 case "$pp_aix_arch_p" in
36 "") pp_debug "can't get processor type from uname -p"
37 pp_aix_arch_p=powerpc
38 pp_aix_arch=R;; # guess (lsattr -l proc0 ??)
39 powerpc) pp_aix_arch=R;;
40 *) pp_aix_arch_p=intel
41 pp_aix_arch=I;; # XXX? verify
42 esac
43
44 case "`/usr/sbin/lsattr -El proc0 -a type -F value`" in
45 PowerPC_POWER*) pp_aix_arch_std=ppc64;;
46 PowerPC*) pp_aix_arch_std=ppc;;
47 *) pp_aix_arch_std=unknown;;
48 esac
49 }
50
51 pp_aix_detect_os () {
52 typeset r v
53
54 r=`uname -r`
55 v=`uname -v`
56 pp_aix_os=aix$v$r
57 }
58
59 #-- pp_aix_version_fix(version) convert version into an AIX version
60 # AIX demands version strings of the form a.b.c.d[.e]
61 pp_aix_version_fix () {
62 typeset v
63 v=`echo $1 | tr -c -d '[0-9].\012'`
64 if test x"$v" != x"$1"; then
65 pp_warn "stripped version '$1' to '$v'"
66 fi
67 case $v in
68 ""|*..*|.*|*.) pp_error "malformed '$1'"
69 echo "0.0.0.0";;
70 *.*.*.*.*)
71 # 5 components are only valid for fileset updates, not base
72 # filesets (full packages). We trim 5+ components down to 4.
73 pp_warn "version '$1' has too many dots for AIX, truncating"
74 echo "$v" | cut -d. -f1-4;;
75 *.*.*.*) echo "$v";;
76 *.*.*) echo "$v.0";;
77 *.*) echo "$v.0.0";;
78 *) echo "$v.0.0.0";;
79 esac
80 }
81
82 #@ pp_aix_select(-user|-root) < file-list:
83 # Selects out the %files lines that are in /usr or /opt (-user)
84 # or otherwise (-root).
85 pp_aix_select () {
86 case "$1" in
87 -user) op="";;
88 -root) op="!";;
89 *) pp_die "pp_aix_select: bad argument";;
90 esac
91 #pp_debug awk '$5 '$op' /^\/(usr|opt)(\/|$)/ { print; }'
92 #awk '$5 '$op' /^\/(usr|opt)(\/|$)/ { print; }'
93 awk $op'($6 ~ /^\/usr\// || $6 ~ /^\/opt\//) { print; }'
94 }
95
96 #@ pp_aix_copy_root($root) < root-file-list:
97 # Copies (links) the files from $pp_destdir into $pp_destdir$root, creating
98 # directories as needed. This is used prior to running the BFF backup.
99 pp_aix_copy_root () {
100 typeset t m o g f p st target
101 while read t m o g f p st; do
102 case "$t" in
103 d) pp_create_dir_if_missing "$1${p%/}";;
104 f) pp_add_transient_file "$1$p"
105 pp_verbose ln "$pp_destdir$p" "$pp_destdir$1$p" ||
106 pp_error "can't link $p into $1";;
107 # s) pp_add_transient_file "$1$p"
108 # #target=`$pp_readlink_fn $pp_destdir$p`
109 # pp_verbose ln -s "$st" "$pp_destdir$1$p" ||
110 # pp_error "can't create symbolic link $p to $st";;
111 *) pp_warn "pp_aix_copy_root: filetype $t not handled";;
112 esac
113 done
114 }
115
116 #@ pp_aix_size() < %files.*: generate the blocksize sums of directories
117 # The directories are derived from a scan of a %files component, then
118 # du and awk are used to add up the total block sizes of files
119 # e.g. the lines
120 # f 644 root root - /usr/bin/ls
121 # f 644 root root - /usr/bin/mv
122 # f 644 root root - /usr/lib/libc.a
123 # could turn into
124 # /usr/bin 61
125 # /usr/lib 115
126 # if the sum size in blocks of ls and mv is 61, and libc.a is 115 blocks.
127 # note that directories themselves are ignored
128
129 pp_aix_size () {
130 typeset prefix t m o g f p st
131
132 prefix="$1"
133 while read t m o g f p st; do
134 case "$t" in f) du -a "$pp_destdir$p";; esac
135 done | sed -e 's!/[^/]*$!!' | sort +1 |
136 awk '{ if ($2 != d)
137 { if (sz) print d,sz;
138 d=$2; sz=0 }
139 sz += $1; }
140 END { if (sz) print d,sz }' |
141 sed -n -e "s!^$pp_destdir!$prefix!p"
142 }
143
144 #@ pp_aix_list([prefix]): prints all the entries relative to .
145 pp_aix_list () {
146 awk '{ print "." pfx $6; }' pfx="$1"
147 }
148
149 #@ pp_aix_make_liblpp(outputdir/liblpp.a workarea/*)
150 pp_aix_make_liblpp () {
151 typeset out dn fl f
152
153 out="$1"; shift
154 dn=`dirname "$2"`
155 fl=
156 for f
157 do
158 case "$f" in "$dn/"*) fl="$fl `basename $f`" ;;
159 *) pp_die "liblpp name $f not in $dn/";; esac
160 done
161 (cd "$dn" && pp_verbose ar -c -g -r "$out" $fl) || pp_error "ar error"
162 }
163
164 #@ pp_aix_make_script(output-script) < script content
165 pp_aix_make_script () {
166 rm -f "$1"
167 echo "#!/bin/sh" > "$1"
168 cat >> "$1"
169 echo "exit 0" >> "$1"
170 chmod +x "$1"
171 }
172
173 #@ pp_aix_inventory(fileset) < %files.xxx
174 # Writes inventory stanzas on stdout
175 pp_aix_inventory () {
176 typeset fileset t m o g f p st type
177
178 fileset="$1"
179 while read t m o g f p st; do
180 case "$p" in *:*) pp_error "path $p contains colon";; esac
181 echo "$p:"
182 case "$t" in
183 f) type=FILE; defm=644 ;;
184 s) type=SYMLINK; defm=777 ;;
185 d) type=DIRECTORY; defm=755 ;;
186 esac
187 echo " type = $type"
188 echo " class = inventory,apply,$fileset"
189 set -- `/bin/ls -ld "$pp_destdir$p" 2>/dev/null`
190 owner=$3 group=$4 size=$5
191 if test x"$m" = x"-"; then m="$defm"; fi
192 if test x"$o" = x"-"; then o="root"; fi
193 if test x"$g" = x"-"; then g="system"; fi
194 echo " owner = $o"
195 echo " group = $g"
196
197 case "$m" in ????)
198 m=`echo $m|sed -e 's/^1/TCB,/' \
199 -e 's/^[23]/TCB,SGID,/' \
200 -e 's/^[45]/TCB,SUID,/' \
201 -e 's/^[67]/TCB,SUID,SGID,/'`;; # vtx bit ignored
202 esac
203 echo " mode = $m"
204 case "$t" in
205 f) if test ! -f "$pp_destdir$p"; then
206 pp_error "$p: missing file"
207 fi
208 case "$flags" in
209 *v*)
210 echo " size = VOLATILE"
211 echo " checksum = VOLATILE"
212 ;;
213 *)
214 if test -r "$pp_destdir$p"; then
215 echo " size = $size"
216 pp_verbose sum -r < "$pp_destdir$p" |
217 sed -e 's/.*/ checksum = "&"/'
218 fi
219 ;;
220 esac;;
221 s)
222 echo " target = $st"
223 ;;
224 esac
225
226 #-- Record ABI types seen
227 case "$t" in
228 f) if test -r "$pp_destdir$p"; then
229 case "`file "$pp_destdir$p"`" in
230 *": executable (RISC System/6000)"*) abi=ppc;;
231 *": 64-bit XCOFF executable"*) abi=ppc64;;
232 *) abi=;;
233 esac
234 if test -n "$abi"; then
235 pp_add_to_list pp_aix_abis_seen $abi
236 fi
237 fi;;
238 esac
239
240 done
241 }
242
243 #@ pp_aix_depend foo.%depend
244 # writes to stdout dependency entries
245 pp_aix_depend ()
246 {
247 if test -s "$1"; then
248 pp_warn "aix dependencies not implemented"
249 fi
250 }
251
252 #@ pp_aix_add_service(service): adds to %post/%preun files service setup
253 # Adds code into package that implements the service
254 pp_aix_add_service () {
255 typeset svc cmd_cmd cmd_arg f
256 svc="$1"
257
258 pp_load_service_vars $svc
259
260 set -- $cmd
261 cmd_cmd="$1"; shift
262 cmd_arg="$*";
263
264 case "$stop_signal" in
265 HUP) stop_signal=1;;
266 INT) stop_signal=2;;
267 QUIT) stop_signal=3;;
268 KILL) stop_signal=9;;
269 TERM) stop_signal=15;;
270 USR1) stop_signal=30;;
271 USR2) stop_signal=31;;
272 "")
273 pp_error "%service $svc: stop_signal not set";;
274 [a-zA-Z]*)
275 pp_error "%service $svc: bad stop_signal ($stop_signal)";;
276 esac
277
278 test -z "$pidfile" || pp_error "aix requires empty pidfile (non daemon)"
279
280 pp_add_component run
281 if test "$user" = "root"; then
282 uid=0
283 else
284 uid="\"\`/usr/bin/id -u $user\`\""
285 fi
286
287
288 #-- add command text to create/remove the service
289 cat <<-. >> $pp_wrkdir/%post.run
290 # install service '$svc'
291 mkssys -s $svc -u $uid -p "$cmd_cmd" ${cmd_arg:+-a "$cmd_arg"} -O -S -n $stop_signal -f 9 ${pp_aix_mkssys_args} ${group:+-G "$group"}
292 .
293
294 #-- add code to start the service on reboot
295 ${pp_aix_init_services_after_install} &&
296 cat <<-. >> $pp_wrkdir/%post.run
297 # start service '$svc' on reboot
298 mkitab "$svc:2:once:/usr/bin/startsrc -s $svc >/dev/console 2>&1"
299 .
300
301 ${pp_aix_start_services_after_install} &&
302 cat <<-. >> $pp_wrkdir/%post.run
303 # start service '$svc' automatically after install
304 startsrc -s $svc
305 .
306
307 ${pp_aix_init_services_after_install} &&
308 pp_prepend $pp_wrkdir/%preun.run <<-.
309 # stop starting service '$svc' at boot
310 rmitab $svc
311 .
312 pp_prepend $pp_wrkdir/%preun.run <<-.
313 # stop service '$svc'
314 stopsrc -s $svc >/dev/null 2>&1
315 # uninstall service '$svc' from SRC
316 rmssys -s $svc
317 .
318 }
319
320 #@ pp_backend_aix(): generate an installable AIX package
321 pp_backend_aix () {
322 typeset briefex instuser instroot svc cmp outbff
323 typeset user_wrkdir root_wrkdir
324 typeset user_files root_files
325
326 test -n "$pp_destdir" ||
327 pp_error "AIX backend requires the '--destdir' option"
328
329 instuser="/usr/lpp/$name"
330 instroot="$instuser/inst_root"
331
332 # Here is the component mapping:
333 # run -> $name.rte ('Run time environment')
334 # doc -> $name.doc (non-standard)
335 # dev -> $name.adt ('Application developer toolkit')
336 # dbg -> $name.diag ('Diagnostics')
337
338 test `echo "$summary" | wc -c ` -gt 40 && pp_error "\$summary too long"
339
340 user_wrkdir=$pp_wrkdir/u
341 root_wrkdir=$pp_wrkdir/r
342 pp_verbose rm -rf $user_wrkdir $root_wrkdir
343 pp_verbose mkdir -p $user_wrkdir $root_wrkdir
344
345 for svc in $pp_services .; do
346 test . = "$svc" && continue
347 pp_aix_add_service $svc
348 done
349
350 {
351 echo "4 $pp_aix_arch I $name {"
352
353 for cmp in $pp_components; do
354 case "$cmp" in
355 run) ex=rte briefex="runtime";;
356 doc) ex=doc briefex="documentation";;
357 dev) ex=adt briefex="developer toolkit";;
358 dbg) ex=diag briefex="diagnostics";;
359 esac
360
361 user_files=$pp_wrkdir/%files.$cmp.u
362 root_files=$pp_wrkdir/%files.$cmp.r
363
364 pp_aix_select -user < $pp_wrkdir/%files.$cmp > $user_files
365 pp_aix_select -root < $pp_wrkdir/%files.$cmp > $root_files
366
367 # Default to USR only unless there are root files,
368 # or a post/pre/check script associated
369 content=U
370 if test -s $root_files \
371 -o -s $pp_wrkdir/%post.$cmp \
372 -o -s $pp_wrkdir/%preun.$cmp \
373 -o -s $pp_wrkdir/%check.$cmp
374 then
375 content=B
376 fi
377
378 if $pp_opt_debug; then
379 echo "$cmp USER %files:"
380 cat $user_files
381 echo "$cmp ROOT %files:"
382 cat $root_files
383 fi >&2
384
385 bosboot=N; pp_contains_any "$pp_aix_bosboot" $cmp && bosboot=b
386
387 echo $name.$ex \
388 ${pp_aix_version:-`pp_aix_version_fix "$version"`} \
389 1 $bosboot $content \
390 $pp_aix_lang "$summary $briefex"
391 echo "["
392
393 pp_aix_depend $pp_wrkdir/%depend.$cmp
394
395 echo "%"
396
397 # generate per-directory size information
398 pp_aix_size < $user_files
399 pp_aix_size $instroot < $root_files
400
401 pp_aix_list < $user_files > $user_wrkdir/$name.$ex.al
402 pp_aix_list $instroot < $root_files >> $user_wrkdir/$name.$ex.al
403 pp_aix_list < $root_files > $root_wrkdir/$name.$ex.al
404
405 if $pp_opt_debug; then
406 echo "$cmp USER $name.$ex.al:"
407 cat $user_wrkdir/$name.$ex.al
408 echo "$cmp ROOT $name.$ex.al:"
409 cat $root_wrkdir/$name.$ex.al
410 fi >&2
411
412 pp_aix_inventory $name.$ex < $user_files \
413 > $user_wrkdir/$name.$ex.inventory
414 pp_aix_inventory $name.$ex < $root_files \
415 > $root_wrkdir/$name.$ex.inventory
416
417 if $pp_opt_debug; then
418 pp_debug "$cmp USER $name.$ex.inventory:"
419 cat $user_wrkdir/$name.$ex.inventory
420 pp_debug "$cmp ROOT $name.$ex.inventory:"
421 cat $root_wrkdir/$name.$ex.inventory
422 fi >&2
423
424 if test x"" != x"$pp_aix_copyright"; then
425 echo "$pp_aix_copyright" > $user_wrkdir/$name.$ex.copyright
426 echo "$pp_aix_copyright" > $root_wrkdir/$name.$ex.copyright
427 fi
428
429 #-- assume that post/pre uninstall scripts only make
430 # sense when installed in a root context
431
432 if test -r $pp_wrkdir/%post.$cmp; then
433 pp_aix_make_script $root_wrkdir/$name.$ex.post_i \
434 < $pp_wrkdir/%post.$cmp
435 fi
436
437 if test -r $pp_wrkdir/%preun.$cmp; then
438 pp_aix_make_script $root_wrkdir/$name.$ex.unpost_i \
439 < $pp_wrkdir/%preun.$cmp
440 fi
441
442 if test -r $pp_wrkdir/%check.$cmp; then
443 #-- if this script exits false, then it should print a msg
444 pp_aix_make_script $root_wrkdir/$name.$ex.pre_i \
445 < $pp_wrkdir/%check.$cmp
446 fi
447
448 # remove empty files
449 for f in $user_wrkdir/$name.$ex.* $root_wrkdir/$name.$ex.*; do
450 if test ! -s "$f"; then
451 pp_debug "removing empty $f"
452 rm -f "$f"
453 fi
454 done
455
456 # copy/link the root files so we can do an easy backup later
457 pp_aix_copy_root $instroot < $root_files
458
459 echo "%"
460 echo "]"
461 done
462 echo "}"
463 } > $pp_wrkdir/lpp_name
464
465 if $pp_opt_debug; then
466 echo "/lpp_name :"
467 cat $pp_wrkdir/lpp_name
468 fi >&2
469
470 #-- copy the /lpp_name file to the destdir
471 pp_add_transient_file /lpp_name
472 cp $pp_wrkdir/lpp_name $pp_destdir/lpp_name
473
474 #-- copy the liblpp.a files under destdir for packaging
475 (cd $user_wrkdir && pp_verbose ar -c -g -r liblpp.a $name.*) ||
476 pp_error "ar error"
477 if test -s $user_wrkdir/liblpp.a; then
478 pp_add_transient_file $instuser/liblpp.a
479 pp_verbose cp $user_wrkdir/liblpp.a $pp_destdir$instuser/liblpp.a ||
480 pp_error "cannot create user liblpp.a"
481 fi
482 (cd $root_wrkdir && pp_verbose ar -c -g -r liblpp.a $name.*) ||
483 pp_error "ar error"
484 if test -s $root_wrkdir/liblpp.a; then
485 pp_add_transient_file $instroot/liblpp.a
486 pp_verbose cp $root_wrkdir/liblpp.a $pp_destdir$instroot/liblpp.a ||
487 pp_error "cannot create root liblpp.a"
488 fi
489
490 { echo ./lpp_name
491 test -s $user_wrkdir/liblpp.a && echo .$instuser/liblpp.a
492 test -s $root_wrkdir/liblpp.a && echo .$instroot/liblpp.a
493 cat $user_wrkdir/$name.*.al # includes the relocated root files!
494 } > $pp_wrkdir/bff.list
495
496 if test -n "$pp_aix_abis_seen" -a x"$pp_aix_arch_std" = x"auto"; then
497 case "$pp_aix_abis_seen" in
498 "ppc ppc64"|"ppc64 ppc")
499 pp_aix_arch_std=ppc64
500 ;;
501 ppc|ppc64)
502 pp_aix_arch_std=$pp_aix_abis_seen
503 ;;
504 *" "*)
505 pp_warn "multiple architectures detected: $pp_aix_abis_seen"
506 pp_aix_arch_std=unknown
507 ;;
508 "")
509 pp_warn "no binary executables detected; using noarch"
510 pp_aix_arch_std=noarch
511 ;;
512 *)
513 pp_warn "unknown architecture detected $pp_aix_abis_seen"
514 pp_aix_arch_std=$pp_aix_abis_seen
515 ;;
516 esac
517 fi
518
519 . $pp_wrkdir/%fixup
520
521 outbff=`pp_backend_aix_names`
522 pp_debug "creating: $pp_wrkdir/$outbff"
523 (cd $pp_destdir && pp_verbose /usr/sbin/backup -i -q -p -f -) \
524 < $pp_wrkdir/bff.list \
525 > $pp_wrkdir/$outbff || pp_error "backup failed"
526 ${SUDO:-sudo} /usr/sbin/installp -l -d $pp_wrkdir/$outbff
527 }
528
529 #@ pp_backend_aix_cleanup(): removes leftover files
530 pp_backend_aix_cleanup () {
531 :
532 }
533
534 #@ pp_backend_aix_names(): print the name of the AIX package
535 #-- prints the output package name without the $pp_wrkdir prefix.
536 # the name always starts with $name-$version
537 pp_backend_aix_names () {
538 echo "$name.${pp_aix_version:-`pp_aix_version_fix "$version"`}.bff"
539 }
540
541 #@ pp_backend_aix_install_script(): generate install script
542 pp_backend_aix_install_script () {
543 typeset pkgname platform
544 #
545 # The script should take a first argument being the
546 # operation; further arguments refer to components or services
547 #
548 # list-components -- lists components in the pkg
549 # install component... -- installs the components
550 # uninstall component... -- uninstalles the components
551 # list-services -- lists the services in the pkg
552 # start service... -- starts the name service
553 # stop service... -- stops the named services
554 # print-platform -- prints the platform group
555 #
556 pkgname="`pp_backend_aix_names`"
557 platform="`pp_backend_aix_probe`" # XXX should be derived from files
558
559 fsets=
560 for cmp in $pp_components; do
561 case "$cmp" in
562 run) ex=rte;;
563 doc) ex=doc;;
564 dev) ex=adt;;
565 dbg) ex=diag;;
566 esac
567 fsets="$fsets $name.$ex"
568 done
569
570 echo '#!/bin/sh'
571 pp_install_script_common
572
573 cat <<-.
574
575 cpt_to_fileset () {
576 test x"\$*" = x"all" &&
577 set -- $pp_components
578 for cpt
579 do
580 case "\$cpt" in
581 run) echo "$name.rte";;
582 doc) echo "$name.doc";;
583 dev) echo "$name.adt";;
584 dbg) echo "$name.diag";;
585 *) usage;;
586 esac
587 done
588 }
589
590 test \$# -eq 0 && usage
591 op="\$1"; shift
592
593 case "\$op" in
594 list-components)
595 test \$# -eq 0 || usage \$op
596 echo "$pp_components"
597 ;;
598 list-services)
599 test \$# -eq 0 || usage \$op
600 echo "$pp_services"
601 ;;
602 list-files)
603 test \$# -ge 1 || usage \$op
604 echo \${PP_PKGDESTDIR:-.}/$pkgname
605 ;;
606 install)
607 test \$# -ge 1 || usage \$op
608 verbose /usr/sbin/installp -acX -V0 -F \
609 -d \${PP_PKGDESTDIR:-.}/$pkgname \
610 \`cpt_to_fileset "\$@"\`
611 ;;
612 uninstall)
613 test \$# -ge 1 || usage \$op
614 verbose /usr/sbin/installp -u -e/dev/null \
615 -V0 \`cpt_to_fileset "\$@"\`
616 ;;
617 start|stop)
618 test \$# -ge 1 || usage \$op
619 ec=0
620 for svc
621 do
622 verbose \${op}src -s \$svc || ec=1
623 done
624 exit \$ec
625 ;;
626 print-platform)
627 echo "$platform"
628 ;;
629 *)
630 usage;;
631 esac
632 .
633 }
634
635 #@ pp_backend_aix_init_svc_vars(): initialise service vars
636 pp_backend_aix_init_svc_vars () {
637 :
638 }
639
640 #@ pp_backend_aix_probe(): print local host identifier
641 pp_backend_aix_probe () {
642 echo "${pp_aix_os}-${pp_aix_arch_std}"
643 }
644
645 #@ pp_backend_aix_vas_platforms(): print the VAS platform identifiers
646 # that might work on this system
647 pp_backend_aix_vas_platforms () {
648 case "${pp_aix_arch_std}" in
649 ppc*) :;;
650 *) pp_die "unknown architecture ${pp_aix_arch_std}";;
651 esac
652 case "${pp_aix_os}" in
653 aix43) echo "aix-43";;
654 aix51) echo "aix-51 aix-43";;
655 aix52) echo "aix-51 aix-43";;
656 aix53) echo "aix-53 aix-51 aix-43";;
657 aix61) echo "aix-53 aix-51 aix-43";;
658 *) pp_die "unknown system ${pp_aix_os}";;
659 esac
660 }

Ted.Percival@quest.com
ViewVC Help
Powered by ViewVC 1.1.2