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