1 #! /bin/sh 2 # 3 # $NetBSD: regpkg,v 1.26 2023/02/11 04:16:57 uki 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 /^@pkgdir/ { 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} '/^@pkgdir/ { 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.26 2023/02/11 04:16:57 uki 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 SHA256:". 669 echo "${line}" 670 file="${DESTDIR}/${line}" 671 if [ -f "${file}" ] && [ -r "${file}" ]; 672 then 673 sha256sum="$(${CKSUM} -a sha256 -n "${file}" \ 674 | ${AWK} '{print $1}' 675 )" 676 echo "@comment SHA256:${sha256sum}" 677 fi 678 ;; 679 esac 680 done <"${PLIST}" 681 } >"${SYSPKG_DB_SUBDIR}/+CONTENTS" 682 683 # 684 # Update ${SYSPKG_DB_TOPDIR}/pkgdb.byfile.db. 685 # 686 { 687 init_db_opts # sets dbfile, dbtype, and db_opts 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 # "@pkgdir ." -> "/" 694 # "@pkgdir foo/bar" -> "/foo/bar" 695 # "@pkgdir ./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} '/^@pkgdir \.\// {gsub("^.", "", $2); print $2; next} 701 /^@pkgdir \.$/ {print "/"; next} 702 /^@pkgdir/ {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 # 726 create_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 # "@pkgdir ." -> ". type=dir" 792 # "@pkgdir foo/bar" -> "./foo/bar type=dir" 793 # "@pkgdir ./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} '/^@pkgdir \.\// {print $2 " type=dir"; next} 800 /^@pkgdir \.$/ {print ". type=dir"; next} 801 /^@pkgdir/ {print "./" $2 " type=dir"; next} 802 /^@/ {next} 803 /^\.\// {print $0; next} 804 /./ {print "./" $0; next}' \ 805 <"${PLIST}" | 806 # Escape some characters to match the new mtree(8) format. 807 # C.f. usr.sbin/mtree/spec.c:vispath() 808 # XXX escape only '[' for now 809 ${SED} -e 's,\[,\\133,g' \ 810 >"${spec1}" 811 812 # If metalog was specified, attributes from metalog override 813 # attributes in the file system. We also fake up an 814 # entry for the ./etc/mtree/set.${pkgset} file. 815 { 816 if [ -n "${metalog}" ]; then 817 ${AWK} -f "${rundir}/join.awk" \ 818 "${spec1}" "${metalog}" 819 ${AWK} -f "${rundir}/join.awk" \ 820 "${spec1}" /dev/stdin <<EOF 821 ./etc/mtree/set.${pkgset} type=file mode=0444 uname=root gname=wheel 822 EOF 823 else 824 cat "${spec1}" 825 fi 826 } >"${spec2}" 827 828 # 829 # If a file or directory to was mentioned explicitly 830 # in ${PLIST} but not mentioned in ${metalog}, then the 831 # file or directory will not be mentioned in ${spec2}. 832 # This is an error, and means that the metalog was 833 # not built correctly. 834 # 835 if [ -n "${metalog}" ]; then 836 names1="${SCRATCH}/names1" 837 names2="${SCRATCH}/names2" 838 ${AWK} '{print $1}' <"${spec1}" | ${SORT} >"${names1}" 839 ${AWK} '{print $1}' <"${spec2}" | ${SORT} >"${names2}" 840 if ${FGREP} -v -f "${names2}" "${spec1}" >/dev/null 841 then 842 cat >&2 <<EOM 843 ${ERRWARN}The metalog file (${metalog}) does not 844 contain entries for the following files or directories 845 which should be part of the ${pkg} syspkg: 846 EOM 847 ${FGREP} -v -f "${names2}" "${spec1}" >&2 848 ${force} || bomb 849 fi 850 if ${FGREP} -v -f "${names1}" "${spec2}" >/dev/null 851 then 852 cat >&2 <<EOM 853 ${ERRWARN}The following lines are in the metalog file 854 (${metalog}), and the corresponding files or directories 855 should be in the ${pkg} syspkg, but something is wrong: 856 EOM 857 ${FGREP} -v -f "${names1}" "${spec2}" >&2 858 bomb 859 fi 860 fi 861 862 # Add lines (tagged "optional") for any implicit directories. 863 # 864 # For example, if we have a file ./foo/bar/baz, then we add 865 # "./foo/bar optional type=dir", "./foo optional type=dir", 866 # and ". optional type=dir", unless those directories were 867 # already mentioned explicitly. 868 # 869 ${AWK} -f "${rundir}/getdirs.awk" "${spec2}" \ 870 | ${SORT} -u >"${specfile_payload}" 871 } 872 873 # Use two pax invocations followed by gzip to create 874 # the tgz file. 875 # 876 # Remove any leading "./" from path names, because that 877 # could confuse tools that work with binary packages. 878 ( 879 cd "${SYSPKG_DB_SUBDIR}" && \ 880 ${PAX} -O -w -d -N"${etcdir}" -M '-s,^\./,,' \ 881 -f "${tarball_uncompressed}" \ 882 <"${specfile_overhead}" \ 883 || bomb 884 ) 885 ( 886 cd "${DESTDIR:-/}" && \ 887 ${PAX} -O -w -d -N"${etcdir}" -M '-s,^\./,,' \ 888 -a -f "${tarball_uncompressed}" \ 889 <"${specfile_payload}" \ 890 || bomb 891 ) 892 ${GZIP_CMD} -9n <"${tarball_uncompressed}" >"${binpkgfile}" || bomb 893 894 # (Extra space is to make message line up with "Registered" message.) 895 if ${verbose}; then 896 echo " Packaged ${binpkgfile}" 897 elif ! ${quiet}; then 898 echo " Packaged ${binpkgfile##*/}" 899 fi 900 901 cleanup_must_delete_binpkgfile=false 902 903 } 904 905 # 906 # do_register_syspkg() registers the syspkg if appropriate. 907 # 908 # If SYSPKG_DB_SUBDIR already exists, that might be an error, depending 909 # on ${force} and ${update} flags. 910 # 911 do_register_syspkg() 912 { 913 # Check that necessary variables are defined 914 [ -n "${SYSPKG_DB_TOPDIR}" ] || bomb 915 [ -n "${SYSPKG_DB_SUBDIR}" ] || bomb 916 917 # Create SYSPKG_DB_TOPDIR if necessary 918 [ -d "${SYSPKG_DB_TOPDIR}" ] || mkdir -p "${SYSPKG_DB_TOPDIR}" || bomb 919 920 # A function to delete db entries referring to any version of ${pkg} 921 delete_old_db_entries() 922 { 923 init_db_opts # sets dbfile, dbtype, and db_opts 924 dblist="${SCRATCH}/dblist" 925 ${DB} ${db_opts} -O "${tab}" "${dbtype}" "${dbfile}" \ 926 | ${AWK} -F "${tab}" '$2 ~ /^'"${pkg}"'-[0-9]/ { print $1 }' \ 927 >"${dblist}" 928 ${DB} -d ${db_opts} -f "${dblist}" "${dbtype}" "${dbfile}" 929 } 930 931 # A function to delete any old version of ${pkg} 932 delete_old_pkg() 933 { 934 pattern="${pkg}-[0-9]*" 935 matches="$(cd "${SYSPKG_DB_TOPDIR}" && echo ${pattern} \ 936 || bomb)" 937 echo >&2 "${NOTE}deleting old pkg (${matches})" 938 cleanup_must_delete_dbsubdir=true 939 delete_old_db_entries 940 ( cd "${SYSPKG_DB_TOPDIR}" && rm -rf ${matches} ) 941 } 942 943 # Check whether another version of ${pkg} is already registered. 944 pattern="${pkg}-[0-9]*" 945 matches="$(cd "${SYSPKG_DB_TOPDIR}" && echo ${pattern} || bomb)" 946 case "${matches}" in 947 *\*) ;; # wildcard did not match anything 948 "${pkg}-${t}") ;; # exact match 949 *) echo >&2 "${ERRWARNNOTE}another version of ${pkg} is already registered" 950 ${verbose} && echo >&2 " in ${SYSPKG_DB_TOPDIR}" 951 ${verbose} && echo >&2 " (while registering ${pkg}-${t})" 952 ${force} || ${update} || bomb 953 delete_old_pkg 954 ;; 955 esac 956 957 # Check whether the desired version of ${pkg} is already registered, 958 # and create it if appropriate. 959 if [ -d "${SYSPKG_DB_SUBDIR}" ]; then 960 echo >&2 "${ERRWARNNOTE}${pkg}-${t} is already registered" 961 ${verbose} && echo >&2 " in ${SYSPKG_DB_TOPDIR}" 962 if ${force}; then 963 delete_old_pkg 964 register_syspkg 965 elif ${update}; then 966 # 967 # If all files in SYSPKG_DB_SUBDIR are newer 968 # than all files in the pkg, then do nothing. 969 # Else delete and re-register the pkg. 970 # 971 [ -n "${newestfile}" ] || init_newestfile 972 if [ -n "${newestfile}" ]; then 973 case "$(${FIND} "${SYSPKG_DB_SUBDIR}" -type f \ 974 ! -newer "${newestfile}" -print)" \ 975 in 976 "") ;; 977 *) 978 echo >&2 "${NOTE}some files are newer but pkg version is unchanged" 979 delete_old_pkg 980 register_syspkg 981 ;; 982 esac 983 984 else 985 # No files in the pkg? (This could happen 986 # if a pkg contains only directories.) 987 # Do nothing (keep the already-registered pkg). 988 : 989 fi 990 else 991 bomb 992 fi 993 else 994 register_syspkg 995 fi 996 } 997 998 # 999 # do_create_syspkg_tgz() creates the binary pkg (*.tgz) if 1000 # appropriate. 1001 # 1002 # If binpkgfile already exists, that might be an error, depending on 1003 # ${force} and ${update} flags. 1004 # 1005 do_create_syspkg_tgz() 1006 { 1007 [ -n "${binpkgfile}" ] || bomb 1008 1009 delete_and_recreate() 1010 { 1011 echo >&2 "${ERRWARNNOTE}deleting and re-creating ${pkg}-${t}.tgz" 1012 rm -f "${binpkgfile}" 1013 create_syspkg_tgz 1014 } 1015 1016 # Check whether another version of ${pkg} already exists. 1017 pattern="${pkg}-[0-9]*" 1018 matches="$(cd "${binpkgdir}" && echo ${pattern} || bomb)" 1019 case "${matches}" in 1020 *\*) ;; # wildcard did not match anything 1021 "${pkg}-${t}.tgz") ;; # exact match 1022 *) echo >&2 "${ERRWARNNOTE}another version of ${pkg} binary pkg already exists" 1023 ${verbose} && echo >&2 " in ${binpkgdir}" 1024 ${verbose} && echo >&2 " (while creating ${pkg}-${t}.tgz)" 1025 # If neither force nor update, this is a fatal error. 1026 # If force but not update, then leave old .tgz in place. 1027 # If update, then delete the old .tgz. 1028 ${force} || ${update} || bomb 1029 if ${update}; then 1030 echo >&2 "${NOTE}deleting old binary pkg (${matches})" 1031 ( cd "${binpkgdir}" && rm -f ${matches} || bomb ) 1032 fi 1033 ;; 1034 esac 1035 1036 # Check whether the desired version of ${pkg} already exists, 1037 # and create it if appropriate. 1038 if [ -e "${binpkgfile}" ]; then 1039 echo >&2 "${ERRWARNNOTE}${pkg}-${t}.tgz already exists" 1040 ${verbose} && echo >&2 " in ${binpkgdir}" 1041 if ${force}; then 1042 delete_and_recreate 1043 elif ${update}; then 1044 # 1045 # If all files in SYSPKG_DB_SUBDIR are older 1046 # than ${binpkgfile}, then do nothing. 1047 # Else delete and re-create the tgz. 1048 # 1049 case "$(${FIND} "${SYSPKG_DB_SUBDIR}" -type f \ 1050 -newer "${binpkgfile}" -print)" \ 1051 in 1052 "") ;; 1053 *) delete_and_recreate ;; 1054 esac 1055 else 1056 bomb 1057 fi 1058 else 1059 create_syspkg_tgz 1060 fi 1061 } 1062 1063 #################### 1064 # begin main program 1065 1066 parse_args ${1+"$@"} 1067 make_PLIST 1068 choose_version_number 1069 SYSPKG_DB_SUBDIR="${SYSPKG_DB_TOPDIR}/${pkg}-${t}" 1070 do_register_syspkg 1071 if [ -n "${binpkgdir}" ]; then 1072 binpkgfile="${binpkgdir}/${pkg}-${t}.tgz" 1073 do_create_syspkg_tgz 1074 fi 1075 1076 exit 0 1077