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