regpkg revision 1.13 1 #! /bin/sh
2 #
3 # $NetBSD: regpkg,v 1.13 2006/01/28 18:42:23 apb Exp $
4 #
5 # Copyright (c) 2003 Alistair G. Crooks. All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 # 1. Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in the
14 # documentation and/or other materials provided with the distribution.
15 # 3. All advertising materials mentioning features or use of this software
16 # must display the following acknowledgement:
17 # This product includes software developed by Alistair G. Crooks.
18 # for the NetBSD project.
19 # 4. The name of the author may not be used to endorse or promote
20 # products derived from this software without specific prior written
21 # permission.
22 #
23 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
24 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
27 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #
35
36 # Usage: regpkg [options] set pkgname
37 #
38 # Registers a syspkg in the database directory,
39 # and optionally creates a binary package.
40 #
41 # Options:
42 # -q Quiet.
43 # -v Verbose.
44 # -f Force.
45 # -m Ignore errors from missing files.
46 # -u Update.
47 # -c Use cached information from ${BUILD_INFO_CACHE}.
48 # -d destdir Sets DESTDIR.
49 # -t binpkgdir Create a binary package (in *.tgz format) in the
50 # specified directory. Without this option, a binary
51 # package is not created.
52 # -M metalog Use the specified metalog file to override file
53 # or directory attributes when creating a binary package.
54 # -N etcdir Use the specified directory for passwd and group files.
55 #
56 # When -f is set: If the desired syspkg already exists, it is overwritten.
57 # When -u is set: If the desired syspkg already exists, it might be
58 # overwritten or left alone, depending on whether it's older
59 # or newer than the files that belong to the syspkg.
60 # When neither -u nor -f are set: It's an error for the desired syspkg
61 # to already exist.
62
63 prog="${0##*/}"
64 toppid=$$
65 rundir="$(dirname "$0")" # ${0%/*} isn't good enough when there's no "/"
66 . "${rundir}/sets.subr"
67
68 bomb()
69 {
70 #echo "${prog}: bomb: start, toppid=${toppid} \$\$=$$"
71 kill ${toppid} # in case we were invoked from a subshell
72 #echo "${prog}: bomb: killed ${toppid}"
73 exit 1
74 }
75
76 # A literal newline
77 nl='
78 '
79 # A literal tab
80 tab=' '
81
82 # Prefixes for error messages, warnings, and important informational
83 # messages.
84 ERROR="${prog}: ERROR: "
85 WARNING="${prog}: WARNING: "
86 NOTE="${prog}: NOTE: "
87 ERRWARN="${ERROR}" # may be changed by "-f" (force) command line flag
88 ERRWARNNOTE="${ERROR}" # may be changed by "-u" (update) command line flag
89
90 #
91 # All temporary files will go in ${SCRATCH}, which will be deleted on
92 # exit.
93 #
94 SCRATCH="$( ${MKTEMP} -d "/var/tmp/${0##*/}.XXXXXX" )"
95 if [ $? -ne 0 -o \! -d "${SCRATCH}" ]; then
96 echo >&2 "${prog}: Could not create scratch directory."
97 bomb
98 fi
99
100 #
101 # cleanup() always deletes the SCRATCH directory, and might also
102 # delete other files or directories.
103 #
104 es=0
105 cleanup_must_delete_binpkgfile=false
106 cleanup_must_delete_dbsubdir=false
107 cleanup ()
108 {
109 trap - 0
110 #echo "${prog}: cleanup start"
111 if ${cleanup_must_delete_binpkgfile:-false} && [ -e "${binpkgfile}" ]
112 then
113 echo >&2 "${prog}: deleting partially-created ${binpkgfile}"
114 rm -f "${binpkgfile}"
115 fi
116 if ${cleanup_must_delete_dbsubdir:-false} \
117 && [ -e "${SYSPKG_DB_SUBDIR}" ]
118 then
119 echo >&2 "${prog}: deleting partially-created ${SYSPKG_DB_SUBDIR}"
120 rm -rf "${SYSPKG_DB_SUBDIR}"
121 fi
122 rm -rf "${SCRATCH}"
123 #echo "${prog}: cleanup done, exit ${es}"
124 exit ${es}
125 }
126 trap 'es=128; cleanup' 1 2 3 13 15 # HUP INT QUIT PIPE TERM
127 trap 'es=$?; cleanup' 0 # EXIT
128
129 #
130 # Parse command line args.
131 #
132 verbose=false
133 verbosity=0
134 quiet=false
135 force=false
136 update=false
137 allowmissing=false
138 DESTDIR="${DESTDIR}"
139 binpkgdir=""
140 metalog=""
141 etcdir=""
142 SYSPKG_DB_TOPDIR=""
143 pkgset=""
144 pkg=""
145 parse_args ()
146 {
147 while [ $# -gt 2 ]; do
148 case "$1" in
149 -q) quiet=true ; verbose=false ;;
150 -v) verbose=true ; quiet=false
151 verbosity=$(( ${verbosity} + 1 ))
152 ;;
153 -f) force=true ;;
154 -u) update=true ;;
155 -m) allowmissing=true ;;
156 -c) # The -c option is ignored. The BUILD_INFO_CACHE
157 # environment variable is used instead.
158 ;;
159 -d) DESTDIR="$2" ; shift ;;
160 -d*) DESTDIR="${1#-?}" ;;
161 -t) binpkgdir="$2" ; shift ;;
162 -t*) binpkgdir="${1#-?}" ;;
163 -M) metalog="$2" ; shift ;;
164 -M*) metalog="${1#-?}" ;;
165 -N) etcdir="$2" ; shift ;;
166 -N*) etcdir="${1#-?}" ;;
167 *) break ;;
168 esac
169 shift
170 done
171 if ${force}; then
172 ERRWARN="${WARNING}"
173 else
174 ERRWARN="${ERROR}"
175 fi
176 if ${update}; then
177 ERRWARNNOTE="${NOTE}"
178 else
179 ERRWARNNOTE="${ERRWARN}"
180 fi
181 DESTDIR="${DESTDIR%/}" # delete trailing "/" if any
182 if [ \! -n "${etcdir}" ]; then
183 etcdir="${DESTDIR}/etc"
184 fi
185 if [ -n "${binpkgdir}" -a \! -d "${binpkgdir}" ]; then
186 echo >&2 "${ERROR}binary pkg directory ${binpkgdir} does not exist"
187 bomb
188 fi
189 #
190 # SYSPKG_DB_TOPDIR is the top level directory for registering
191 # syspkgs. It defaults to ${DESTDIR}/var/db/syspkg, but can be
192 # overridden by environment variables SYSPKG_DBDIR or PKG_DBDIR.
193 #
194 # Note that this corresponds to the default value of PKG_DBDIR
195 # set in .../distrib/syspkg/mk/bsd.syspkg.mk.
196 #
197 SYSPKG_DB_TOPDIR="${SYSPKG_DBDIR:-${PKG_DBDIR:-${DESTDIR}/var/db/syspkg}}"
198
199 if [ $# -ne 2 ]; then
200 echo "Usage: regpkg [options] set pkgname"
201 bomb
202 fi
203
204 pkgset="$1"
205 pkg="$2"
206 }
207
208 #
209 # make_PLIST() creates a skeleton PLIST from the pkgset description.
210 #
211 # The result is stored in the file ${PLIST}.
212 #
213 PLIST="${SCRATCH}/PLIST"
214 make_PLIST ()
215 {
216 if ${verbose} ; then
217 echo "Making PLIST for \"${pkg}\" package (part of ${pkgset} set)"
218 fi
219 prefix="${DESTDIR:-/}"
220 realprefix=/
221 ${HOST_SH} "${rundir}/makeplist" -p "${prefix}" -I "${realprefix}" \
222 "${pkgset}" "${pkg}" \
223 >"${PLIST}" 2>"${SCRATCH}/makeplist-errors"
224 if ${EGREP} -v '^DEBUG:' "${SCRATCH}/makeplist-errors" ; then
225 # "find" invoked from makeplist sometimes reports
226 # errors about missing files or directories, and
227 # makeplist ignores the errors. Catch them here.
228 echo >&2 "${ERROR}makeplist reported errors for ${pkg}:"
229 cat >&2 "${SCRATCH}/makeplist-errors"
230 echo >&2 "${ERROR}see above for errors from makeplist"
231 if ${allowmissing}; then
232 echo >&2 "${prog}: ${NOTE}: ignoring above errors, due to '-m' option."
233 else
234 ${force} || bomb
235 fi
236 fi
237 }
238
239 #
240 # init_allfiles() converts the PLIST (which contains relative filenames)
241 # into a list of absolute filenames. Directories are excluded from the
242 # result.
243 #
244 # The result is stored in the variable ${allfiles}.
245 #
246 allfiles=''
247 init_allfiles ()
248 {
249 [ -f "${PLIST}" ] || make_PLIST
250 allfiles="$( ${AWK} '
251 BEGIN { destdir = "'"${DESTDIR%/}"'" }
252 /^@cwd/ { prefix = $2; next }
253 /^@dirrm/ { next }
254 { printf("%s%s%s\n", destdir, prefix, $0) }' "${PLIST}" )"
255 }
256
257 #
258 # init_newestfile() finds the newest file (most recent mtime).
259 #
260 # The result is stored in the variable ${newestfile}.
261 #
262 newestfile=''
263 init_newestfile ()
264 {
265 [ -s "${allfiles}" ] || init_allfiles
266 # We assume no shell special characters in ${allfiles},
267 # and spaces only between file names, not inside file names.
268 # This should be safe, because it has no no user-specified parts.
269 newestfile="$( ${LS} -1dt ${allfiles} | ${SED} '1q' )"
270 }
271
272 #
273 # Various ways of getting parts of the syspkg version number:
274 #
275 # get_osvers() - get the OS version number from osrelease.sh or $(uname -r),
276 # return it in ${osvers}, and set ${method}.
277 # get_tinyvers() - get the tiny version number from the "versions" file,
278 # and return it in ${tinyvers}. Does not set ${method}.
279 # get_newest_rcsid_date() - get the newest RCS date,
280 # and return it in ${newest}. Does not set ${method}.
281 # get_newest_mtime_date() - get the newest file modification date,
282 # and return it in ${newest}. Does not set ${method}.
283 # get_newest_date() - get date from rcsid or mtime, return it in ${newest},
284 # and set ${method}.
285 #
286 get_osvers ()
287 {
288 if [ -f ../../sys/conf/osrelease.sh ]; then
289 osvers="$(${HOST_SH} ../../sys/conf/osrelease.sh)"
290 method=osreleases
291 else
292 osvers="$(${UNAME} -r)"
293 method=uname
294 fi
295 #echo "${osvers}"
296 }
297 get_tinyvers ()
298 {
299 tinyvers="$( ${AWK} '$1 ~ '/"${pkg}"/' { print $2 }' \
300 "${rundir}/versions" )"
301 case "${tinyvers}" in
302 "") tinyvers=0
303 ;;
304 esac
305 #echo "${tinyvers}"
306 }
307 get_newest_rcsid_date()
308 {
309 [ -s "${allfiles}" ] || init_allfiles
310
311 # Old RCS identifiers might have 2-digit years, so we match both
312 # YY/MM/DD and YYYY/MM/DD. We also try to deal with the Y10K
313 # problem by allowing >4 digit years.
314 newest=0
315 case "${allfiles}" in
316 "") ;;
317 *) newest="$( ${IDENT} ${allfiles} 2>/dev/null | ${AWK} '
318 BEGIN { last = 0 }
319 $2 == "crt0.c,v" { next }
320 NF == 8 && \
321 $4 ~ /^[0-9][0-9]\/[0-9][0-9]\/[0-9][0-9]$/ \
322 { t = "19" $4; gsub("/", "", t);
323 if (t > last) last = t; }
324 NF == 8 && \
325 $4 ~ /^[0-9][0-9][0-9][0-9][0-9]*\/[0-9][0-9]\/[0-9][0-9]$/ \
326 { t = $4; gsub("/", "", t);
327 if (t > last) last = t; }
328 END { print last }' )"
329 method=ident
330 ;;
331 esac
332 #echo "${newest}"
333 }
334 get_newest_mtime_date ()
335 {
336 [ -s "${newestfile}" ] || init_newestfile
337
338 # We could simplify the awk program to take advantage of the
339 # fact thet it should have exactly one line of input.
340 newest="$( ${ENV_CMD} TZ=UTC LOCALE=C ${LS} -lT "${newestfile}" \
341 | ${AWK} '
342 BEGIN { newest = 0 }
343 {
344 t = $9 "";
345 if ($6 == "Jan") t = t "01";
346 if ($6 == "Feb") t = t "02";
347 if ($6 == "Mar") t = t "03";
348 if ($6 == "Apr") t = t "04";
349 if ($6 == "May") t = t "05";
350 if ($6 == "Jun") t = t "06";
351 if ($6 == "Jul") t = t "07";
352 if ($6 == "Aug") t = t "08";
353 if ($6 == "Sep") t = t "09";
354 if ($6 == "Oct") t = t "10";
355 if ($6 == "Nov") t = t "11";
356 if ($6 == "Dec") t = t "12";
357 if ($7 < 10) t = t "0";
358 t = t $7;
359 #these next two lines add the 24h clock onto the date
360 #gsub(":", "", $8);
361 #t = sprintf("%s.%4.4s", t, $8);
362 if (t > newest) newest = t;
363 }
364 END { print newest }' )"
365 #echo "${newest}"
366 }
367 get_newest_date ()
368 {
369 get_newest_rcsid_date
370 case "${newest}" in
371 ""|0) get_newest_mtime_date
372 method=ls
373 ;;
374 *) method=rcsid
375 ;;
376 esac
377 #echo "${newest}"
378 }
379
380 #
381 # choose_version_number() chooses the syspkg version number,
382 # by concatenating several components (OS version, syspkg "tiny"
383 # version and date). We end up with something like
384 # osvers="3.99.15", tinyvers="0", newest="20060104",
385 # and t="3.99.15.0.20060104".
386 #
387 # The result is stored in the variables ${t} and ${method}.
388 #
389 method=''
390 t=''
391 choose_version_number ()
392 {
393 get_osvers ; m1="${method}"
394 get_tinyvers # does not set ${method}
395 get_newest_date ; m2="${method}"
396 t="${osvers}.${tinyvers}.${newest}"
397 method="${m1}.${m2}"
398
399 # print version number that we're using
400 if ${verbose}; then
401 echo "${pkg} - ${t} version using ${method} method"
402 fi
403 }
404
405 #
406 # init_db_opts() sets the dbfile, dbtype and db_opts variables,
407 # used for accessing the pkgdb.byfile.db database.
408 #
409 init_db_opts ()
410 {
411 dbfile="${SYSPKG_DB_TOPDIR}/pkgdb.byfile.db"
412 dbtype="btree"
413 db_opts=''
414 : ${TARGET_ENDIANNESS:="$( arch_to_endian "${MACHINE_ARCH}" )"}
415 case "${TARGET_ENDIANNESS}" in
416 4321) db_opts="${db_opts} -E B" # big-endian
417 ;;
418 1234) db_opts="${db_opts} -E L" # little-endian
419 ;;
420 *)
421 echo >&2 "${WARNING}Unknown or unsupported target endianness"
422 echo >&2 "${NOTE}Using host endianness"
423 ;;
424 esac
425 if ${update} || ${force} ; then
426 # overwriting an existing entry is not an error
427 db_opts="${db_opts} -R"
428 fi
429 if [ ${verbosity} -lt 2 ]; then
430 # don't print all the keys added to the database
431 db_opts="${db_opts} -q"
432 fi
433 }
434
435 #
436 # print_dir_exec_lines outputs an "@exec install" line for each
437 # directory in ${PLIST}
438 #
439 print_dir_exec_lines ()
440 {
441 local dir uname gname mode
442 local dot_slash_dir
443 local no_dot_dir
444 local word line
445 ${AWK} '/^@dirrm/ { print $2 }' <"${PLIST}" | \
446 ${SORT} | \
447 while read dir ; do
448 # Sanitise the name. ${dir} could be an absolute or
449 # relative name, with or without a leading "./".
450 # ${dot_slash_dir} always has a leading "./" (except when
451 # it's exactly equal to "."). ${no_dot_dir} never has a
452 # leading "." or "/" (except when it's exactly equal to
453 # ".").
454 case "${dir}" in
455 .|./|/) dot_slash_dir=. ;;
456 ./*) dot_slash_dir="${dir}" ;;
457 /*) dot_slash_dir=".${dir}" ;;
458 *) dot_slash_dir="./${dir}" ;;
459 esac
460 no_dot_dir="${dot_slash_dir#./}"
461 # Get the directory's owner, group, and mode
462 # from the live file system, or let it be overridden
463 # by the metalog.
464 eval "$( ${STAT} -f 'uname=%Su gname=%Sg mode=%#OLp' \
465 "${DESTDIR}/${dot_slash_dir}" )"
466 if [ -n "${metalog}" ]; then
467 line="$( echo "${dot_slash_dir}" | \
468 ${AWK} -f "${rundir}/join.awk" \
469 /dev/stdin "${metalog}" )"
470 for word in ${line} ; do
471 case "${word}" in
472 uname=*|gname=*|mode=*) eval "${word}" ;;
473 esac
474 done
475 fi
476 # XXX: Work around yet another pkg_add bug: @cwd lines
477 # do not actually cause the working directory to change,
478 # so file names in @exec lines need to be qualified by
479 # %D, which (in our case, since we know there's an
480 # "@cwd /" line) will be the dir name passed to
481 # "pkg_add -p PREFIX".
482 case "${no_dot_dir}" in
483 .) d="%D" ;;
484 *) d="%D/${no_dot_dir}" ;;
485 esac
486 cat <<EOF
487 @exec install -d -o ${uname} -g ${gname} -m ${mode} ${d}
488 EOF
489 done
490 }
491
492 #
493 # register_syspkg() registers the syspkg in ${SYSPKG_DB_TOPDIR}.
494 # This involves creating the subdirectory ${SYSPKG_DB_SUBDIR}
495 # and populating it with several files.
496 #
497 register_syspkg ()
498 {
499 cleanup_must_delete_dbsubdir=true
500 [ -n "${SYSPKG_DB_SUBDIR}" ] || bomb
501 mkdir -p "${SYSPKG_DB_SUBDIR}"
502
503 #
504 # Guess what versions of other packages to depend on.
505 #
506 # If we are using the OS version as part of the pkg
507 # version, then depend on any version ">=${osvers}". For
508 # example, etc-sys-etc-1.6ZI.0.20040206 might depend on
509 # base-sys-root>=1.6ZI.
510 #
511 # Failing that, depend on any version "-[0-9]*".
512 #
513 # XXX: We could extend the format of the "deps" file to carry
514 # this sort of information, so we wouldn't have to guess.
515 #
516 case "${t}" in
517 ${osvers}.*) depversion=">=${osvers}" ;;
518 *) depversion="-[0-9]*" ;;
519 esac
520
521 #
522 # Add the dependencies.
523 #
524 # We always add a "@pkgdep" line for each prerequisite package.
525 #
526 # If the prerequisite pkg is already registered (as it should be
527 # if our caller is doing things in the right order), then we put
528 # its exact version number in a "@blddep" line.
529 #
530 ${AWK} '$1 ~ '/"${pkg}"/' { print $2 }' "${rundir}/deps" | ${SORT} | \
531 while read depname ; do
532 # ${pkgdepglob} is a shell glob pattern that should match
533 # any version of a pkg. ${pkgdep} uses the special syntax
534 # for pkg dependencies, and is not usable as a shell
535 # glob pattern.
536 pkgdepglob="${depname}-[0-9]*"
537 pkgdep="${depname}${depversion}"
538 echo "@pkgdep ${pkgdep}"
539 blddep="$( cd "${SYSPKG_DB_TOPDIR}" && echo ${pkgdepglob} \
540 || bomb )"
541 case "${blddep}" in
542 *\*) # pkgdepglob did not match anything
543 echo >&2 "${WARNING}${pkg} depends on '${pkgdep}' but there is no matching syspkg in ${SYSPKG_DB_TOPDIR}"
544 ;;
545 *\ *) # pkgdepglob matched more than once.
546 echo >&2 "${ERRWARN}${pkg} depends on '${pkgdep}' but there are multiple matching syspkgs in ${SYSPKG_DB_TOPDIR}"
547 ${force} || bomb
548 # If ${force} is set, then assume that the last
549 # match is the most recent.
550 # XXX: This might be wrong, because of
551 # differences between lexical sorting and
552 # numeric sorting.
553 lastmatch="${blddep##* }"
554 echo "@blddep ${lastmatch}"
555 ;;
556 *) # exactly one match.
557 # XXX: We ignore the possibility that the
558 # version we found via ${pkgdepglob} might not
559 # satisfy ${pkgdep}. We could conceivably use
560 # "pkg_admin pmatch" to check, but that's not a
561 # host tool so we can't assume that it will be
562 # available.
563 echo "@blddep ${blddep}"
564 ;;
565 esac
566 done >>"${PLIST}"
567
568 # create the comment (should be one line)
569 comment="$( ${AWK} '$1 ~ '/"${pkg}"/' \
570 { print substr($0, length($1) + 2) }' \
571 "${rundir}/comments" )"
572 case "${comment}" in
573 "") echo >&2 "${WARNING}no comment for \"${pkg}\" (using placeholder)"
574 comment="System package for ${pkg}"
575 ;;
576 *"${nl}"*)
577 echo >&2 "${ERRWARN}multi-line comment for \"${pkg}\""
578 ${force} || bomb
579 ;;
580 esac
581 echo "${comment}" > "${SYSPKG_DB_SUBDIR}/+COMMENT"
582
583 # create the description (could be multiple lines)
584 descr="$( ${AWK} '$1 ~ '/"${pkg}"/' {
585 print substr($0, length($1) + 2) }' \
586 "${rundir}/descrs" )"
587 case "${descr}" in
588 "") echo >&2 "${WARNING}no description for \"${pkg}\" (re-using comment)" 2>&1
589 descr="${comment}"
590 ;;
591 esac
592 echo "${descr}" > "${SYSPKG_DB_SUBDIR}/+DESC"
593 ${PRINTF} "\nHomepage:\nhttp://www.NetBSD.org/\n" >> "${SYSPKG_DB_SUBDIR}/+DESC"
594
595 # create the build information
596 if [ x"${BUILD_INFO_CACHE}" = x ]; then
597 {
598 # These variables describe the build
599 # environment, not the target.
600 echo "OPSYS=$(${UNAME} -s)"
601 echo "OS_VERSION=$(${UNAME} -r)"
602 ${MAKE} -f- all <<EOF
603 .include <bsd.own.mk>
604 all:
605 @echo OBJECT_FMT=${OBJECT_FMT}
606 @echo MACHINE_ARCH=${MACHINE_ARCH}
607 @echo MACHINE_GNU_ARCH=${MACHINE_GNU_ARCH}
608 EOF
609 # XXX: what's the point of reporting _PKGTOOLS_VER
610 # when we roll everything by hand without using
611 # the pkg tools?
612 echo "_PKGTOOLS_VER=$(${PKG_CREATE} -V)"
613 } > "${SYSPKG_DB_SUBDIR}/+BUILD_INFO"
614 else
615 cp "${BUILD_INFO_CACHE}" "${SYSPKG_DB_SUBDIR}/+BUILD_INFO"
616 fi
617
618 # test for attributes
619 args=""
620 attrs="$( ${AWK} '$1 ~ '/"${pkg}"/' { \
621 print substr($0, length($1) + 2) }' \
622 "${rundir}/attrs" )"
623 for a in "${attrs}"; do
624 case "${attrs}" in
625 "") ;;
626 preserve)
627 echo "${pkg}-${t}" >"${SYSPKG_DB_SUBDIR}/+PRESERVE"
628 args="${args} -n ${SYSPKG_DB_SUBDIR}/+PRESERVE"
629 ;;
630 esac
631 done
632
633 #
634 # Create ${SYSPKGSIR}/+CONTENTS from ${PLIST}, by adding an
635 # "@name" line and a lot of "@comment MD5:" lines.
636 #
637 {
638 rcsid='$NetBSD: regpkg,v 1.13 2006/01/28 18:42:23 apb Exp $'
639 utcdate="$( ${ENV_CMD} TZ=UTC LOCALE=C \
640 ${DATE} '+%Y-%m-%d %H:%M' )"
641 user="${USER:-root}"
642 host="$( ${HOSTNAME} )"
643 echo "@name ${pkg}-${t}"
644 echo "@comment Packaged at ${utcdate} UTC by ${user}@${host}"
645 echo "@comment Packaged using ${prog} ${rcsid}"
646 # XXX: "option extract-in-place" might help to get
647 # pkg_add to create directories.
648 # XXX: no, it doesn't work. Yet another pkg_add bug.
649 ## echo "@option extract-in-place"
650 # Move the @pkgdep and @blddep lines up, so that
651 # they are easy to see when people do "less
652 # ${DESTDIR}/var/db/syspkg/*/+CONTENTS".
653 ${EGREP} '^(@pkgdep|@blddep)' "${PLIST}" || true
654 # Now do the remainder of the file.
655 while read line ; do
656 case "${line}" in
657 @pkgdep*|@blddep*)
658 # already handled by grep above
659 ;;
660 @cwd*)
661 # There should be exactly one @cwd line.
662 # Just after it, add an "@exec mkdir"
663 # line for every directory. This is to
664 # work around a pkg-add bug (see
665 # <http://mail-index.NetBSD.org/tech-pkg/2003/12/11/0018.html>)
666 echo "${line}"
667 print_dir_exec_lines
668 ;;
669 @*)
670 # just pass through all other @foo lines
671 echo "${line}"
672 ;;
673 *)
674 # This should be a file name. Pass it
675 # through, and append "@comment MD5:".
676 # XXX why not SHA256 ?
677 echo "${line}"
678 file="${DESTDIR}${line}"
679 if [ -f "${file}" -a -r "${file}" ];
680 then
681 md5sum="$( ${CKSUM} -n -m "${file}" \
682 | ${AWK} '{print $1}'
683 )"
684 echo "@comment MD5:${md5sum}"
685 fi
686 ;;
687 esac
688 done <"${PLIST}"
689 } >"${SYSPKG_DB_SUBDIR}/+CONTENTS"
690
691 #
692 # Update ${SYSPKG_DB_TOPDIR}/pkgdb.byfile.db.
693 #
694 {
695 init_db_opts # sets dbfile, dbtype, and db_opts
696
697 # Transform ${PLIST} into a form to be used as keys in
698 # ${dbfile}. The results look like absolute paths,
699 # but they are really relative to ${DESTDIR}.
700 #
701 # "@dirrm ." -> "/"
702 # "@dirrm foo/bar" -> "/foo/bar"
703 # "@dirrm ./foo/bar" -> "/foo/bar"
704 # "foo/bar/baz" -> "/foo/bar/baz"
705 # "./foo/bar/baz" -> "/foo/bar/baz"
706 #
707 dblist="${SCRATCH}/dblist"
708 ${AWK} '/^@dirrm \.\// {gsub("^.", "", $2); print $2; next}
709 /^@dirrm \.$/ {print "/"; next}
710 /^@dirrm/ {print "/" $2; next}
711 /^@/ {next}
712 /^\.\// {gsub("^.", "", $0); print $0; next}
713 /./ {print "/" $0; next}' \
714 <"${PLIST}" >"${dblist}"
715 # Add all the path names to the database.
716 ${AWK} '{print $1 "\t" "'"${pkg}-${t}"'"}' <"${dblist}" \
717 | ${DB} -w ${db_opts} -F "${tab}" -f - "${dbtype}" "${dbfile}"
718 }
719
720 if ${verbose} ; then
721 echo "Registered ${pkg}-${t} in ${SYSPKG_DB_TOPDIR}"
722 elif ! ${quiet} ; then
723 echo "Registered ${pkg}-${t}"
724 fi
725
726 cleanup_must_delete_dbsubdir=false
727 }
728
729 #
730 # create_syspkg_tgz() creates the *.tgz file for the package.
731 #
732 # The output file is ${binpkgdir}/${pkg}-${t}.tgz.
733 #
734 create_syspkg_tgz ()
735 {
736 #
737 # pkg_create does not understand metalog files, so we have to
738 # use pax directly.
739 #
740 # We create two specfiles: specfile_overhead describes the
741 # special files that are part of the package system's metadata
742 # (+CONTENTS, +COMMENT, +DESCR, and more); and specfile_payload
743 # describes the files and directories that we actually want as
744 # part of the package's payload.
745 #
746 # We then use the specfiles to create a compressed tarball that
747 # contains both the overhead files and the payload files.
748 #
749 # There's no trivial way to get a single pax run to do
750 # everything we want, so we run pax twice, with a different
751 # working directory and a different specfile each time.
752 #
753 # We could conceivably make clever use of pax's "-s" option to
754 # get what we want from a single pax run with a single (more
755 # complicated) specfile, but the extra trouble doesn't seem
756 # warranted.
757 #
758 cleanup_must_delete_binpkgfile=true
759 specfile_overhead="${SCRATCH}/spec_overhead"
760 specfile_payload="${SCRATCH}/spec_payload"
761 tarball_uncompressed="${SCRATCH}/tarball_uncompressed"
762
763 # Create a specfile for all the overhead files (+CONTENTS and
764 # friends).
765 {
766 plusnames_first="${SCRATCH}/plusnames_first"
767 plusnames_rest="${SCRATCH}/plusnames_rest"
768
769 # Ensure that the first few files are in the same order
770 # that "pkg_create" would have used, just in case anything
771 # depends on that. Other files in alphabetical order.
772 SHOULD_BE_FIRST="+CONTENTS +COMMENT +DESC"
773 (
774 cd "${SYSPKG_DB_SUBDIR}" || bomb
775 for file in ${SHOULD_BE_FIRST}; do
776 [ -e "./${file}" ] && echo "${file}"
777 done >"${plusnames_first}"
778 ${LS} -1 | ${FGREP} -v -f "${plusnames_first}" \
779 >"${plusnames_rest}" \
780 || true
781 )
782
783 # Convert the file list to specfile format, and override the
784 # uid/gid/mode.
785 {
786 echo ". optional type=dir"
787 ${AWK} '{print "./" $0 " type=file uid=0 gid=0 mode=0444"
788 }' "${plusnames_first}" "${plusnames_rest}"
789 } >"${specfile_overhead}"
790 }
791
792 # Create a specfile for the payload of the package.
793 {
794 spec1="${SCRATCH}/spec1"
795 spec2="${SCRATCH}/spec2"
796
797 # Transform ${PLIST} into simple specfile format:
798 #
799 # "@dirrm ." -> ". type=dir"
800 # "@dirrm foo/bar" -> "./foo/bar type=dir"
801 # "@dirrm ./foo/bar" -> "./foo/bar type=dir"
802 # "foo/bar/baz" -> "./foo/bar/baz"
803 # "./foo/bar/baz" -> "./foo/bar/baz"
804 #
805 # Ignores @cwd lines. This should be safe, given how
806 # makeplist works.
807 ${AWK} '/^@dirrm \.\// {print $2 " type=dir" ; next}
808 /^@dirrm \.$/ {print ". type=dir"; next}
809 /^@dirrm/ {print "./" $2 " type=dir" ; next}
810 /^@/ {next}
811 /^\.\// {print $0; next}
812 /./ {print "./" $0; next}' \
813 <"${PLIST}" >"${spec1}"
814
815 # If metalog was specified, attributes from metalog override
816 # attributes in the file system. We also fake up an
817 # entry for the ./etc/mtree/set.${pkgset} file.
818 {
819 if [ -n "${metalog}" ]; then
820 ${AWK} -f "${rundir}/join.awk" \
821 "${spec1}" "${metalog}"
822 ${AWK} -f "${rundir}/join.awk" \
823 "${spec1}" /dev/stdin <<EOF
824 ./etc/mtree/set.${pkgset} type=file mode=0444 uname=root gname=wheel
825 EOF
826 else
827 cat "${spec1}"
828 fi
829 } >"${spec2}"
830
831 #
832 # If a file or directory to was mentioned explicitly
833 # in ${PLIST} but not mentioned in ${metalog}, then the
834 # file or directory will not be mentioned in ${spec2}.
835 # This is an error, and means that the metalog was
836 # not built correctly.
837 #
838 if [ -n "${metalog}" ]; then
839 names1="${SCRATCH}/names1"
840 names2="${SCRATCH}/names2"
841 ${AWK} '{print $1}' <"${spec1}" | ${SORT} >"${names1}"
842 ${AWK} '{print $1}' <"${spec2}" | ${SORT} >"${names2}"
843 if ${FGREP} -v -f "${names2}" "${spec1}" >/dev/null
844 then
845 cat >&2 <<EOM
846 ${ERRWARN}The metalog file (${metalog}) does not
847 contain entries for the following files or directories
848 which should be part of the ${pkg} syspkg:
849 EOM
850 ${FGREP} -v -f "${names2}" "${spec1}" >&2
851 ${force} || bomb
852 fi
853 if ${FGREP} -v -f "${names1}" "${spec2}" >/dev/null
854 then
855 cat >&2 <<EOM
856 ${ERRWARN}The following lines are in the metalog file
857 (${metalog}), and the corresponding files or directories
858 should be in the ${pkg} syspkg, but something is wrong:
859 EOM
860 ${FGREP} -v -f "${names1}" "${spec2}" >&2
861 bomb
862 fi
863 fi
864
865 # Add lines (tagged "optional") for any implicit directories.
866 #
867 # For example, if we have a file ./foo/bar/baz, then we add
868 # "./foo/bar optional type=dir", "./foo optional type=dir",
869 # and ". optional type=dir", unless those directories were
870 # already mentioned explicitly.
871 #
872 ${AWK} -f "${rundir}/getdirs.awk" "${spec2}" \
873 | ${SORT} -u >"${specfile_payload}"
874 }
875
876 # Use two pax invocations followed by gzip to create
877 # the tgz file.
878 #
879 # Remove any leading "./" from path names, because that
880 # could confuse tools that work with binary packages.
881 (
882 cd "${SYSPKG_DB_SUBDIR}" && \
883 ${PAX} -O -w -d -N"${etcdir}" -M '-s,^\./,,' \
884 -f "${tarball_uncompressed}" \
885 <"${specfile_overhead}" \
886 || bomb
887 )
888 (
889 cd "${DESTDIR:-/}" && \
890 ${PAX} -O -w -d -N"${etcdir}" -M '-s,^\./,,' \
891 -a -f "${tarball_uncompressed}" \
892 <"${specfile_payload}" \
893 || bomb
894 )
895 ${GZIP_CMD} -9 <"${tarball_uncompressed}" >"${binpkgfile}" || bomb
896
897 # (Extra space is to make message line up with "Registered" message.)
898 if ${verbose} ; then
899 echo " Packaged ${binpkgfile}"
900 elif ! ${quiet} ; then
901 echo " Packaged ${binpkgfile##*/}"
902 fi
903
904 cleanup_must_delete_binpkgfile=false
905
906 }
907
908 #
909 # do_register_syspkg() registers the syspkg if appropriate.
910 #
911 # If SYSPKG_DB_SUBDIR already exists, that might be an error, depending
912 # on ${force} and ${update} flags.
913 #
914 do_register_syspkg ()
915 {
916 # Check that necessary variables are defined
917 [ -n "${SYSPKG_DB_TOPDIR}" ] || bomb
918 [ -n "${SYSPKG_DB_SUBDIR}" ] || bomb
919
920 # Create SYSPKG_DB_TOPDIR if necessary
921 [ -d "${SYSPKG_DB_TOPDIR}" ] || mkdir -p "${SYSPKG_DB_TOPDIR}" || bomb
922
923 # A function to delete db entries referring to any version of ${pkg}
924 delete_old_db_entries ()
925 {
926 init_db_opts # sets dbfile, dbtype, and db_opts
927 dblist="${SCRATCH}/dblist"
928 ${DB} ${db_opts} -O "${tab}" "${dbtype}" "${dbfile}" \
929 | ${AWK} -F "${tab}" '$2 ~ /^'"${pkg}"'-[0-9]/ { print $1 }' \
930 >"${dblist}"
931 ${DB} -d ${db_opts} -f "${dblist}" "${dbtype}" "${dbfile}"
932 }
933
934 # A function to delete any old version of ${pkg}
935 delete_old_pkg ()
936 {
937 pattern="${pkg}-[0-9]*"
938 matches="$( cd "${SYSPKG_DB_TOPDIR}" && echo ${pattern} \
939 || bomb )"
940 echo >&2 "${NOTE}deleting old pkg (${matches})"
941 cleanup_must_delete_dbsubdir=true
942 delete_old_db_entries
943 ( cd "${SYSPKG_DB_TOPDIR}" && rm -rf ${matches} )
944 }
945
946 # Check whether another version of ${pkg} is already registered.
947 pattern="${pkg}-[0-9]*"
948 matches="$( cd "${SYSPKG_DB_TOPDIR}" && echo ${pattern} || bomb )"
949 case "${matches}" in
950 *\*) ;; # wildcard did not match anything
951 "${pkg}-${t}") ;; # exact match
952 *) echo >&2 "${ERRWARNNOTE}another version of ${pkg} is already registered"
953 ${verbose} && echo >&2 " in ${SYSPKG_DB_TOPDIR}"
954 ${verbose} && echo >&2 " (while registering ${pkg}-${t})"
955 ${force} || ${update} || bomb
956 delete_old_pkg
957 ;;
958 esac
959
960 # Check whether the desired version of ${pkg} is already registered,
961 # and create it if appropriate.
962 if [ -d "${SYSPKG_DB_SUBDIR}" ]; then
963 echo >&2 "${ERRWARNNOTE}${pkg}-${t} is already registered"
964 ${verbose} && echo >&2 " in ${SYSPKG_DB_TOPDIR}"
965 if ${force}; then
966 delete_old_pkg
967 register_syspkg
968 elif ${update}; then
969 #
970 # If all files in SYSPKG_DB_SUBDIR are newer
971 # than all files in the pkg, then do nothing.
972 # Else delete and re-register the pkg.
973 #
974 [ -n "${newestfile}" ] || init_newestfile
975 if [ -n "${newestfile}" ]; then
976 case "$( ${FIND} "${SYSPKG_DB_SUBDIR}" -type f \
977 ! -newer "${newestfile}" -print )" \
978 in
979 "") ;;
980 *)
981 echo >&2 "${NOTE}some files are newer but pkg version is unchanged"
982 delete_old_pkg
983 register_syspkg
984 ;;
985 esac
986
987 else
988 # No files in the pkg? (This could happen
989 # if a pkg contains only directories.)
990 # Do nothing (keep the already-registered pkg).
991 fi
992 else
993 bomb
994 fi
995 else
996 register_syspkg
997 fi
998 }
999
1000 #
1001 # do_create_syspkg_tgz() creates the the binary pkg (*.tgz) if
1002 # appropriate.
1003 #
1004 # If binpkgfile already exists, that might be an error, depending on
1005 # ${force} and ${update} flags.
1006 #
1007 do_create_syspkg_tgz ()
1008 {
1009 [ -n "${binpkgfile}" ] || bomb
1010
1011 delete_and_recreate ()
1012 {
1013 echo >&2 "${ERRWARNNOTE}deleting and re-creating ${pkg}-${t}.tgz"
1014 rm -f "${binpkgfile}"
1015 create_syspkg_tgz
1016 }
1017
1018 # Check whether another version of ${pkg} already exists.
1019 pattern="${pkg}-[0-9]*"
1020 matches="$( cd "${binpkgdir}" && echo ${pattern} || bomb )"
1021 case "${matches}" in
1022 *\*) ;; # wildcard did not match anything
1023 "${pkg}-${t}.tgz") ;; # exact match
1024 *) echo >&2 "${ERRWARNNOTE}another version of ${pkg} binary pkg already exists"
1025 ${verbose} && echo >&2 " in ${binpkgdir}"
1026 ${verbose} && echo >&2 " (while creating ${pkg}-${t}.tgz)"
1027 # If neither force nor update, this is a fatal error.
1028 # If force but not update, then leave old .tgz in place.
1029 # If update, then delete the old .tgz.
1030 ${force} || ${update} || bomb
1031 if ${update}; then
1032 echo >&2 "${NOTE}deleting old binary pkg (${matches})"
1033 ( cd "${binpkgdir}" && rm -f ${matches} || bomb )
1034 fi
1035 ;;
1036 esac
1037
1038 # Check whether the desired version of ${pkg} already exists,
1039 # and create it if appropriate.
1040 if [ -e "${binpkgfile}" ]; then
1041 echo >&2 "${ERRWARNNOTE}${pkg}-${t}.tgz already exists"
1042 ${verbose} && echo >&2 " in ${binpkgdir}"
1043 if ${force}; then
1044 delete_and_recreate
1045 elif ${update}; then
1046 #
1047 # If all files in SYSPKG_DB_SUBDIR are older
1048 # than ${binpkgfile}, then do nothing.
1049 # Else delete and re-create the tgz.
1050 #
1051 case "$( ${FIND} "${SYSPKG_DB_SUBDIR}" -type f \
1052 -newer "${binpkgfile}" -print )" \
1053 in
1054 "") ;;
1055 *) delete_and_recreate ;;
1056 esac
1057 else
1058 bomb
1059 fi
1060 else
1061 create_syspkg_tgz
1062 fi
1063 }
1064
1065 ####################
1066 # begin main program
1067
1068 parse_args ${1+"$@"}
1069 make_PLIST
1070 choose_version_number
1071 SYSPKG_DB_SUBDIR="${SYSPKG_DB_TOPDIR}/${pkg}-${t}"
1072 do_register_syspkg
1073 if [ -n "${binpkgdir}" ]; then
1074 binpkgfile="${binpkgdir}/${pkg}-${t}.tgz"
1075 do_create_syspkg_tgz
1076 fi
1077
1078 exit 0
1079