regpkg revision 1.11 1 #! /bin/sh
2 #
3 # $NetBSD: regpkg,v 1.11 2006/01/08 10:24:04 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 # print_dir_exec_lines outputs an "@exec install" line for each
407 # directory in ${PLIST}
408 #
409 print_dir_exec_lines ()
410 {
411 local dir uname gname mode
412 local dot_slash_dir
413 local no_dot_dir
414 local word line
415 ${AWK} '/^@dirrm/ { print $2 }' <"${PLIST}" | \
416 ${SORT} | \
417 while read dir ; do
418 # Sanitise the name. ${dir} could be an absolute or
419 # relative name, with or without a leading "./".
420 # ${dot_slash_dir} always has a leading "./" (except when
421 # it's exactly equal to "."). ${no_dot_dir} never has a
422 # leading "." or "/" (except when it's exactly equal to
423 # ".").
424 case "${dir}" in
425 .|./|/) dot_slash_dir=. ;;
426 ./*) dot_slash_dir="${dir}" ;;
427 /*) dot_slash_dir=".${dir}" ;;
428 *) dot_slash_dir="./${dir}" ;;
429 esac
430 no_dot_dir="${dot_slash_dir#./}"
431 # Get the directory's owner, group, and mode
432 # from the live file system, or let it be overridden
433 # by the metalog.
434 eval "$( ${STAT} -f 'uname=%Su gname=%Sg mode=%#OLp' \
435 "${DESTDIR}/${dot_slash_dir}" )"
436 if [ -n "${metalog}" ]; then
437 line="$( echo "${dot_slash_dir}" | \
438 ${AWK} -f "${rundir}/join.awk" \
439 /dev/stdin "${metalog}" )"
440 for word in ${line} ; do
441 case "${word}" in
442 uname=*|gname=*|mode=*) eval "${word}" ;;
443 esac
444 done
445 fi
446 # XXX: Work around yet another pkg_add bug: @cwd lines
447 # do not actually cause the working directory to change,
448 # so file names in @exec lines need to be qualified by
449 # %D, which (in our case, since we know there's an
450 # "@cwd /" line) will be the dir name passed to
451 # "pkg_add -p PREFIX".
452 case "${no_dot_dir}" in
453 .) d="%D" ;;
454 *) d="%D/${no_dot_dir}" ;;
455 esac
456 cat <<EOF
457 @exec install -d -o ${uname} -g ${gname} -m ${mode} ${d}
458 EOF
459 done
460 }
461
462 #
463 # register_syspkg() registers the syspkg in ${SYSPKG_DB_TOPDIR}.
464 # This involves creating the subdirectory ${SYSPKG_DB_SUBDIR}
465 # and populating it with several files.
466 #
467 register_syspkg ()
468 {
469 cleanup_must_delete_dbsubdir=true
470 [ -n "${SYSPKG_DB_SUBDIR}" ] || bomb
471 mkdir -p "${SYSPKG_DB_SUBDIR}"
472
473 #
474 # Guess what versions of other packages to depend on.
475 #
476 # If we are using the OS version as part of the pkg
477 # version, then depend on any version ">=${osvers}". For
478 # example, etc-sys-etc-1.6ZI.0.20040206 might depend on
479 # base-sys-root>=1.6ZI.
480 #
481 # Failing that, depend on any version "-[0-9]*".
482 #
483 # XXX: We could extend the format of the "deps" file to carry
484 # this sort of information, so we wouldn't have to guess.
485 #
486 case "${t}" in
487 ${osvers}.*) depversion=">=${osvers}" ;;
488 *) depversion="-[0-9]*" ;;
489 esac
490
491 #
492 # Add the dependencies.
493 #
494 # We always add a "@pkgdep" line for each prerequisite package.
495 #
496 # If the prerequisite pkg is already registered (as it should be
497 # if our caller is doing things in the right order), then we put
498 # its exact version number in a "@blddep" line.
499 #
500 ${AWK} '$1 ~ '/"${pkg}"/' { print $2 }' "${rundir}/deps" | ${SORT} | \
501 while read depname ; do
502 # ${pkgdepglob} is a shell glob pattern that should match
503 # any version of a pkg. ${pkgdep} uses the special syntax
504 # for pkg dependencies, and is not usable as a shell
505 # glob pattern.
506 pkgdepglob="${depname}-[0-9]*"
507 pkgdep="${depname}${depversion}"
508 echo "@pkgdep ${pkgdep}"
509 blddep="$( cd "${SYSPKG_DB_TOPDIR}" && echo ${pkgdepglob} \
510 || bomb )"
511 case "${blddep}" in
512 *\*) # pkgdepglob did not match anything
513 echo >&2 "${WARNING}${pkg} depends on '${pkgdep}' but there is no matching syspkg in ${SYSPKG_DB_TOPDIR}"
514 ;;
515 *\ *) # pkgdepglob matched more than once.
516 echo >&2 "${ERRWARN}${pkg} depends on '${pkgdep}' but there are multiple matching syspkgs in ${SYSPKG_DB_TOPDIR}"
517 ${force} || bomb
518 # If ${force} is set, then assume that the last
519 # match is the most recent.
520 # XXX: This might be wrong, because of
521 # differences between lexical sorting and
522 # numeric sorting.
523 lastmatch="${blddep##* }"
524 echo "@blddep ${lastmatch}"
525 ;;
526 *) # exactly one match.
527 # XXX: We ignore the possibility that the
528 # version we found via ${pkgdepglob} might not
529 # satisfy ${pkgdep}. We could conceivably use
530 # "pkg_admin pmatch" to check, but that's not a
531 # host tool so we can't assume that it will be
532 # available.
533 echo "@blddep ${blddep}"
534 ;;
535 esac
536 done >>"${PLIST}"
537
538 # create the comment (should be one line)
539 comment="$( ${AWK} '$1 ~ '/"${pkg}"/' \
540 { print substr($0, length($1) + 2) }' \
541 "${rundir}/comments" )"
542 case "${comment}" in
543 "") echo >&2 "${WARNING}no comment for \"${pkg}\" (using placeholder)"
544 comment="System package for ${pkg}"
545 ;;
546 *"${nl}"*)
547 echo >&2 "${ERRWARN}multi-line comment for \"${pkg}\""
548 ${force} || bomb
549 ;;
550 esac
551 echo "${comment}" > "${SYSPKG_DB_SUBDIR}/+COMMENT"
552
553 # create the description (could be multiple lines)
554 descr="$( ${AWK} '$1 ~ '/"${pkg}"/' {
555 print substr($0, length($1) + 2) }' \
556 "${rundir}/descrs" )"
557 case "${descr}" in
558 "") echo >&2 "${WARNING}no description for \"${pkg}\" (re-using comment)" 2>&1
559 descr="${comment}"
560 ;;
561 esac
562 echo "${descr}" > "${SYSPKG_DB_SUBDIR}/+DESC"
563 ${PRINTF} "\nHomepage:\nhttp://www.NetBSD.org/\n" >> "${SYSPKG_DB_SUBDIR}/+DESC"
564
565 # create the build information
566 if [ x"${BUILD_INFO_CACHE}" = x ]; then
567 {
568 # These variables describe the build
569 # environment, not the target.
570 echo "OPSYS=$(${UNAME} -s)"
571 echo "OS_VERSION=$(${UNAME} -r)"
572 ${MAKE} -f- all <<EOF
573 .include <bsd.own.mk>
574 all:
575 @echo OBJECT_FMT=${OBJECT_FMT}
576 @echo MACHINE_ARCH=${MACHINE_ARCH}
577 @echo MACHINE_GNU_ARCH=${MACHINE_GNU_ARCH}
578 EOF
579 # XXX: what's the point of reporting _PKGTOOLS_VER
580 # when we roll everything by hand without using
581 # the pkg tools?
582 echo "_PKGTOOLS_VER=$(${PKG_CREATE} -V)"
583 } > "${SYSPKG_DB_SUBDIR}/+BUILD_INFO"
584 else
585 cp "${BUILD_INFO_CACHE}" "${SYSPKG_DB_SUBDIR}/+BUILD_INFO"
586 fi
587
588 # test for attributes
589 args=""
590 attrs="$( ${AWK} '$1 ~ '/"${pkg}"/' { \
591 print substr($0, length($1) + 2) }' \
592 "${rundir}/attrs" )"
593 for a in "${attrs}"; do
594 case "${attrs}" in
595 "") ;;
596 preserve)
597 echo "${pkg}-${t}" >"${SYSPKG_DB_SUBDIR}/+PRESERVE"
598 args="${args} -n ${SYSPKG_DB_SUBDIR}/+PRESERVE"
599 ;;
600 esac
601 done
602
603 #
604 # Create ${SYSPKGSIR}/+CONTENTS from ${PLIST}, by adding an
605 # "@name" line and a lot of "@comment MD5:" lines.
606 #
607 {
608 rcsid='$NetBSD: regpkg,v 1.11 2006/01/08 10:24:04 apb Exp $'
609 utcdate="$( ${ENV_CMD} TZ=UTC LOCALE=C \
610 ${DATE} '+%Y-%m-%d %H:%M' )"
611 user="${USER:-root}"
612 host="$( ${HOSTNAME} )"
613 echo "@name ${pkg}-${t}"
614 echo "@comment Packaged at ${utcdate} UTC by ${user}@${host}"
615 echo "@comment Packaged using ${prog} ${rcsid}"
616 # XXX: "option extract-in-place" might help to get
617 # pkg_add to create directories.
618 # XXX: no, it doesn't work. Yet another pkg_add bug.
619 ## echo "@option extract-in-place"
620 # Move the @pkgdep and @blddep lines up, so that
621 # they are easy to see when people do "less
622 # ${DESTDIR}/var/db/syspkg/*/+CONTENTS".
623 ${EGREP} '^(@pkgdep|@blddep)' "${PLIST}" || true
624 # Now do the remainder of the file.
625 while read line ; do
626 case "${line}" in
627 @pkgdep*|@blddep*)
628 # already handled by grep above
629 ;;
630 @cwd*)
631 # There should be exactly one @cwd line.
632 # Just after it, add an "@exec mkdir"
633 # line for every directory. This is to
634 # work around a pkg-add bug (see
635 # <http://mail-index.NetBSD.org/tech-pkg/2003/12/11/0018.html>)
636 echo "${line}"
637 print_dir_exec_lines
638 ;;
639 @*)
640 # just pass through all other @foo lines
641 echo "${line}"
642 ;;
643 *)
644 # This should be a file name. Pass it
645 # through, and append "@comment MD5:".
646 # XXX why not SHA256 ?
647 echo "${line}"
648 file="${DESTDIR}${line}"
649 if [ -f "${file}" -a -r "${file}" ];
650 then
651 md5sum="$( ${CKSUM} -n -m "${file}" \
652 | ${AWK} '{print $1}'
653 )"
654 echo "@comment MD5:${md5sum}"
655 fi
656 ;;
657 esac
658 done <"${PLIST}"
659 } >"${SYSPKG_DB_SUBDIR}/+CONTENTS"
660
661 #
662 # Update ${SYSPKG_DB_TOPDIR}/pkgdb.byfile.db.
663 #
664 {
665 dbfile="${SYSPKG_DB_TOPDIR}/pkgdb.byfile.db"
666 dbtype="btree"
667 db_opts=''
668 # XXX: cross builds should really add "-E B" or "-E L"
669 # (big-endian or little-endian) to db_opts, but we don't
670 # do that yet.
671 if ${update} || ${force} ; then
672 # overwriting an existing entry is not an error
673 db_opts="${db_opts} -R"
674 fi
675 if [ ${verbosity} -lt 2 ]; then
676 # don't print all the keys added to the database
677 db_opts="${db_opts} -q"
678 fi
679
680 # Transform ${PLIST} into a form to be used as keys in
681 # ${dbfile}. The results look like absolute paths,
682 # but they are really relative to ${DESTDIR}.
683 #
684 # "@dirrm ." -> "/"
685 # "@dirrm foo/bar" -> "/foo/bar"
686 # "@dirrm ./foo/bar" -> "/foo/bar"
687 # "foo/bar/baz" -> "/foo/bar/baz"
688 # "./foo/bar/baz" -> "/foo/bar/baz"
689 #
690 dblist="${SCRATCH}/dblist"
691 ${AWK} '/^@dirrm \.\// {gsub("^.", "", $2); print $2; next}
692 /^@dirrm \.$/ {print "/"; next}
693 /^@dirrm/ {print "/" $2; next}
694 /^@/ {next}
695 /^\.\// {gsub("^.", "", $0); print $0; next}
696 /./ {print "/" $0; next}' \
697 <"${PLIST}" >"${dblist}"
698 # Add all the path names to the database.
699 ${AWK} '{print $1 "\t" "'"${pkg}-${t}"'"}' <"${dblist}" \
700 | ${DB} -w ${db_opts} -F "${tab}" -f - "${dbtype}" "${dbfile}"
701 }
702
703 if ${verbose} ; then
704 echo "Registered ${pkg}-${t} in ${SYSPKG_DB_TOPDIR}"
705 elif ! ${quiet} ; then
706 echo "Registered ${pkg}-${t}"
707 fi
708
709 cleanup_must_delete_dbsubdir=false
710 }
711
712 #
713 # create_syspkg_tgz() creates the *.tgz file for the package.
714 #
715 # The output file is ${binpkgdir}/${pkg}-${t}.tgz.
716 #
717 create_syspkg_tgz ()
718 {
719 #
720 # pkg_create does not understand metalog files, so we have to
721 # use pax directly.
722 #
723 # We create two specfiles: specfile_overhead describes the
724 # special files that are part of the package system's metadata
725 # (+CONTENTS, +COMMENT, +DESCR, and more); and specfile_payload
726 # describes the files and directories that we actually want as
727 # part of the package's payload.
728 #
729 # We then use the specfiles to create a compressed tarball that
730 # contains both the overhead files and the payload files.
731 #
732 # There's no trivial way to get a single pax run to do
733 # everything we want, so we run pax twice, with a different
734 # working directory and a different specfile each time.
735 #
736 # We could conceivably make clever use of pax's "-s" option to
737 # get what we want from a single pax run with a single (more
738 # complicated) specfile, but the extra trouble doesn't seem
739 # warranted.
740 #
741 cleanup_must_delete_binpkgfile=true
742 specfile_overhead="${SCRATCH}/spec_overhead"
743 specfile_payload="${SCRATCH}/spec_payload"
744 tarball_uncompressed="${SCRATCH}/tarball_uncompressed"
745
746 # Create a specfile for all the overhead files (+CONTENTS and
747 # friends).
748 {
749 plusnames_first="${SCRATCH}/plusnames_first"
750 plusnames_rest="${SCRATCH}/plusnames_rest"
751
752 # Ensure that the first few files are in the same order
753 # that "pkg_create" would have used, just in case anything
754 # depends on that. Other files in alphabetical order.
755 SHOULD_BE_FIRST="+CONTENTS +COMMENT +DESC"
756 (
757 cd "${SYSPKG_DB_SUBDIR}" || bomb
758 for file in ${SHOULD_BE_FIRST}; do
759 [ -e "./${file}" ] && echo "${file}"
760 done >"${plusnames_first}"
761 ${LS} -1 | ${FGREP} -v -f "${plusnames_first}" \
762 >"${plusnames_rest}" \
763 || true
764 )
765
766 # Convert the file list to specfile format, and override the
767 # uid/gid/mode.
768 {
769 echo ". optional type=dir"
770 ${AWK} '{print "./" $0 " type=file uid=0 gid=0 mode=0444"
771 }' "${plusnames_first}" "${plusnames_rest}"
772 } >"${specfile_overhead}"
773 }
774
775 # Create a specfile for the payload of the package.
776 {
777 spec1="${SCRATCH}/spec1"
778 spec2="${SCRATCH}/spec2"
779
780 # Transform ${PLIST} into simple specfile format:
781 #
782 # "@dirrm ." -> ". type=dir"
783 # "@dirrm foo/bar" -> "./foo/bar type=dir"
784 # "@dirrm ./foo/bar" -> "./foo/bar type=dir"
785 # "foo/bar/baz" -> "./foo/bar/baz"
786 # "./foo/bar/baz" -> "./foo/bar/baz"
787 #
788 # Ignores @cwd lines. This should be safe, given how
789 # makeplist works.
790 ${AWK} '/^@dirrm \.\// {print $2 " type=dir" ; next}
791 /^@dirrm \.$/ {print ". type=dir"; next}
792 /^@dirrm/ {print "./" $2 " type=dir" ; next}
793 /^@/ {next}
794 /^\.\// {print $0; next}
795 /./ {print "./" $0; next}' \
796 <"${PLIST}" >"${spec1}"
797
798 # If metalog was specified, attributes from metalog override
799 # attributes in the file system. We also fake up an
800 # entry for the ./etc/mtree/set.${pkgset} file.
801 {
802 if [ -n "${metalog}" ]; then
803 ${AWK} -f "${rundir}/join.awk" \
804 "${spec1}" "${metalog}"
805 ${AWK} -f "${rundir}/join.awk" \
806 "${spec1}" /dev/stdin <<EOF
807 ./etc/mtree/set.${pkgset} type=file mode=0444 uname=root gname=wheel
808 EOF
809 else
810 cat "${spec1}"
811 fi
812 } >"${spec2}"
813
814 #
815 # If a file or directory to was mentioned explicitly
816 # in ${PLIST} but not mentioned in ${metalog}, then the
817 # file or directory will not be mentioned in ${spec2}.
818 # This is an error, and means that the metalog was
819 # not built correctly.
820 #
821 if [ -n "${metalog}" ]; then
822 names1="${SCRATCH}/names1"
823 names2="${SCRATCH}/names2"
824 ${AWK} '{print $1}' <"${spec1}" | ${SORT} >"${names1}"
825 ${AWK} '{print $1}' <"${spec2}" | ${SORT} >"${names2}"
826 if ${FGREP} -v -f "${names2}" "${spec1}" >/dev/null
827 then
828 cat >&2 <<EOM
829 ${ERRWARN}The metalog file (${metalog}) does not
830 contain entries for the following files or directories
831 which should be part of the ${pkg} syspkg:
832 EOM
833 ${FGREP} -v -f "${names2}" "${spec1}" >&2
834 ${force} || bomb
835 fi
836 if ${FGREP} -v -f "${names1}" "${spec2}" >/dev/null
837 then
838 cat >&2 <<EOM
839 ${ERRWARN}The following lines are in the metalog file
840 (${metalog}), and the corresponding files or directories
841 should be in the ${pkg} syspkg, but something is wrong:
842 EOM
843 ${FGREP} -v -f "${names1}" "${spec2}" >&2
844 bomb
845 fi
846 fi
847
848 # Add lines (tagged "optional") for any implicit directories.
849 #
850 # For example, if we have a file ./foo/bar/baz, then we add
851 # "./foo/bar optional type=dir", "./foo optional type=dir",
852 # and ". optional type=dir", unless those directories were
853 # already mentioned explicitly.
854 #
855 ${AWK} -f "${rundir}/getdirs.awk" "${spec2}" \
856 | ${SORT} -u >"${specfile_payload}"
857 }
858
859 # Use two pax invocations followed by gzip to create
860 # the tgz file.
861 #
862 # Remove any leading "./" from path names, because that
863 # could confuse tools that work with binary packages.
864 (
865 cd "${SYSPKG_DB_SUBDIR}" && \
866 ${PAX} -O -w -d -N"${etcdir}" -M '-s,^\./,,' \
867 -f "${tarball_uncompressed}" \
868 <"${specfile_overhead}" \
869 || bomb
870 )
871 (
872 cd "${DESTDIR:-/}" && \
873 ${PAX} -O -w -d -N"${etcdir}" -M '-s,^\./,,' \
874 -a -f "${tarball_uncompressed}" \
875 <"${specfile_payload}" \
876 || bomb
877 )
878 ${GZIP_CMD} -9 <"${tarball_uncompressed}" >"${binpkgfile}" || bomb
879
880 # (Extra space is to make message line up with "Registered" message.)
881 if ${verbose} ; then
882 echo " Packaged ${binpkgfile}"
883 elif ! ${quiet} ; then
884 echo " Packaged ${binpkgfile##*/}"
885 fi
886
887 cleanup_must_delete_binpkgfile=false
888
889 }
890
891 #
892 # do_register_syspkg() registers the syspkg if appropriate.
893 #
894 # If SYSPKG_DB_SUBDIR already exists, that might be an error, depending
895 # on ${force} and ${update} flags.
896 #
897 do_register_syspkg ()
898 {
899 # Check that necessary variables are defined
900 [ -n "${SYSPKG_DB_TOPDIR}" ] || bomb
901 [ -n "${SYSPKG_DB_SUBDIR}" ] || bomb
902
903 # Create SYSPKG_DB_TOPDIR if necessary
904 [ -d "${SYSPKG_DB_TOPDIR}" ] || mkdir -p "${SYSPKG_DB_TOPDIR}" || bomb
905
906 # A function to delete and re-register a syspkg
907 delete_and_reregister ()
908 {
909 echo >&2 "${ERRWARNNOTE}deleting and re-registering ${pkg}-${t}"
910 cleanup_must_delete_dbsubdir=true
911 rm -rf "${SYSPKG_DB_SUBDIR}"
912 register_syspkg
913 }
914
915 # Check whether another version of ${pkg} is already registered.
916 pattern="${pkg}-[0-9]*"
917 matches="$( cd "${SYSPKG_DB_TOPDIR}" && echo ${pattern} || bomb )"
918 case "${matches}" in
919 *\*) ;; # wildcard did not match anything
920 "${pkg}-${t}") ;; # exact match
921 *) echo >&2 "${ERRWARN}another version of ${pkg} is already registered in ${SYSPKG_DB_TOPDIR} (while creating ${pkg}-${t})"
922 ${force} || bomb
923 ;;
924 esac
925
926 # Check whether the desired version of ${pkg} is already registered,
927 # and create it if appropriate.
928 if [ -d "${SYSPKG_DB_SUBDIR}" ]; then
929 echo >&2 "${ERRWARNNOTE}${pkg}-${t} is already registered"
930 ${verbose} && echo >&2 " in ${SYSPKG_DB_TOPDIR}"
931 if ${force}; then
932 delete_and_reregister
933 elif ${update}; then
934 #
935 # If all files in SYSPKG_DB_SUBDIR are newer
936 # than all files in the pkg, then do nothing.
937 # Else delete and re-register the pkg.
938 #
939 [ -n "${newestfile}" ] || init_newestfile
940 if [ -n "${newestfile}" ]; then
941 case "$( ${FIND} "${SYSPKG_DB_SUBDIR}" -type f \
942 ! -newer "${newestfile}" -print )" \
943 in
944 "") ;;
945 *) delete_and_reregister ;;
946 esac
947
948 else
949 # No files in the pkg? (This could happen
950 # if a pkg contains only directories.)
951 # Do nothing.
952 fi
953 else
954 bomb
955 fi
956 else
957 register_syspkg
958 fi
959 }
960
961 #
962 # do_create_syspkg_tgz() creates the the binary pkg (*.tgz) if
963 # appropriate.
964 #
965 # If binpkgfile already exists, that might be an error, depending on
966 # ${force} and ${update} flags.
967 #
968 do_create_syspkg_tgz ()
969 {
970 [ -n "${binpkgfile}" ] || bomb
971
972 delete_and_recreate ()
973 {
974 echo >&2 "${ERRWARNNOTE}deleting and re-creating ${pkg}-${t}.tgz"
975 rm -f "${binpkgfile}"
976 create_syspkg_tgz
977 }
978
979 # Check whether another version of ${pkg} already exists.
980 pattern="${pkg}-[0-9]*"
981 matches="$( cd "${binpkgdir}" && echo ${pattern} || bomb )"
982 case "${matches}" in
983 *\*) ;; # wildcard did not match anything
984 "${pkg}-${t}.tgz") ;; # exact match
985 *) echo >&2 "${ERRWARN}another version of ${pkg} already exists in ${binpkgdir} (while creating ${pkg}-${t}.tgz)"
986 ${force} || bomb
987 ;;
988 esac
989
990 # Check whether the desired version of ${pkg} already exists,
991 # and create it if appropriate.
992 if [ -e "${binpkgfile}" ]; then
993 echo >&2 "${ERRWARNNOTE}${pkg}-${t}.tgz already exists"
994 ${verbose} && echo >&2 " in ${binpkgdir}"
995 if ${force}; then
996 delete_and_recreate
997 elif ${update}; then
998 #
999 # If all files in SYSPKG_DB_SUBDIR are older
1000 # than ${binpkgfile}, then do nothing.
1001 # Else delete and re-create the tgz.
1002 #
1003 case "$( ${FIND} "${SYSPKG_DB_SUBDIR}" -type f \
1004 -newer "${binpkgfile}" -print )" \
1005 in
1006 "") ;;
1007 *) delete_and_recreate ;;
1008 esac
1009 else
1010 bomb
1011 fi
1012 else
1013 create_syspkg_tgz
1014 fi
1015 }
1016
1017 ####################
1018 # begin main program
1019
1020 parse_args ${1+"$@"}
1021 make_PLIST
1022 choose_version_number
1023 SYSPKG_DB_SUBDIR="${SYSPKG_DB_TOPDIR}/${pkg}-${t}"
1024 do_register_syspkg
1025 if [ -n "${binpkgdir}" ]; then
1026 binpkgfile="${binpkgdir}/${pkg}-${t}.tgz"
1027 do_create_syspkg_tgz
1028 fi
1029
1030 exit 0
1031