############################################################### smallutils proc smallyes { setvar YES = "${1-y}" while echo $YES { : ; } } proc in_path { local OLD_IFS="$IFS" setvar IFS = "":"" for dir in [$PATH] { if test -x "$dir/$1" { setvar IFS = "$OLD_IFS" return 0 } } setvar IFS = "$OLD_IFS" return 1 } ############################################################### interaction proc error { # local err="$1" local name="$2" local fmt="$3" shift; shift; shift if test $USE_DEBIANINSTALLER_INTERACTION { shell {echo "E: $name" for x in [@ARGV] { echo "EA: $x"; } echo "EF: $fmt"} >&4 } else { shell {printf "E: $fmt\n" @ARGV} >&4 } exit $err } proc warning { # local name="$1" local fmt="$2" shift; shift if test $USE_DEBIANINSTALLER_INTERACTION { shell {echo "W: $name" for x in [@ARGV] { echo "WA: $x"; } echo "WF: $fmt"} >&4 } else { printf "W: $fmt\n" @ARGV >&4 } } proc info { # local name="$1" local fmt="$2" shift; shift if test $USE_DEBIANINSTALLER_INTERACTION { shell {echo "I: $name" for x in [@ARGV] { echo "IA: $x"; } echo "IF: $fmt"} >&4 } else { printf "I: $fmt\n" @ARGV >&4 } } setvar PROGRESS_NOW = '0' setvar PROGRESS_END = '0' setvar PROGRESS_NEXT = """" setvar PROGRESS_WHAT = """" proc progress_next { setvar PROGRESS_NEXT = "$1" } proc wgetprogress { test ! $VERBOSE && setvar QSWITCH = ""-q"" local ret=0 if test $USE_DEBIANINSTALLER_INTERACTION && test $PROGRESS_NEXT { wget @ARGV 2>&1 >/dev/null | $PKGDETAILS "WGET%" $PROGRESS_NOW $PROGRESS_NEXT $PROGRESS_END >&3 setvar ret = "$Status" } else { wget $QSWITCH @ARGV setvar ret = "$Status" } return $ret } proc progress { # local now="$1" local end="$2" local name="$3" local fmt="$4" shift; shift; shift; shift if test $USE_DEBIANINSTALLER_INTERACTION { setvar PROGRESS_NOW = "$now" setvar PROGRESS_END = "$end" setvar PROGRESS_NEXT = """" shell {echo "P: $now $end $name" for x in [@ARGV] { echo "PA: $x"; } echo "PF: $fmt"} >&3 } } proc dpkg_progress { # UNPACKING|CONFIGURING local now="$1" local end="$2" local name="$3" local desc="$4" local action="$5" local expect= if test $action = UNPACKING { setvar expect = 'half-installed' } elif test $action = CONFIGURING { setvar expect = 'half-configured' } proc dp { setvar now = """$(($now + ${1:-1}))" } setvar exitcode = '0' while read status pkg qstate { if test $status = "EXITCODE" { setvar exitcode = "$pkg" continue } test $qstate = $expect || continue match $qstate { with half-installed dp; progress $now $end $name $desc info $action "Unpacking %s..." ${pkg%:} setvar expect = 'unpacked' with unpacked setvar expect = 'half-installed' with half-configured dp; progress $now $end $name $desc info $action "Configuring %s..." ${pkg%:} setvar expect = 'installed' with installed setvar expect = 'half-configured' } } return $exitcode } ############################################################# set variables proc default_mirror { setvar DEF_MIRROR = "$1" } setvar FINDDEBS_NEEDS_INDICES = 'false' proc finddebs_style { match $1 { with hardcoded with from-indices setvar FINDDEBS_NEEDS_INDICES = 'true' with * error 1 BADFINDDEBS "unknown finddebs style" } } proc mk_download_dirs { if test $DLDEST = "apt_dest" { mkdir -p "$TARGET/$APTSTATE/lists/partial" mkdir -p "$TARGET/var/cache/apt/archives/partial" } } proc download_style { match $1 { with apt if test $2 = "var-state" { setvar APTSTATE = 'var/state/apt' } else { setvar APTSTATE = 'var/lib/apt' } setvar DLDEST = 'apt_dest' export APTSTATE DLDEST DEBFOR with * error 1 BADDLOAD "unknown download style" } } proc keyring { if test -z $KEYRING { if test -e $1 { setvar KEYRING = "$1" } elif test -z $DISABLE_KEYRING { if test -n $DEF_HTTPS_MIRROR && test -z $USER_MIRROR && test -z $FORCE_KEYRING { info KEYRING "Keyring file not available at %s; switching to https mirror %s" $1 $DEF_HTTPS_MIRROR setvar USER_MIRROR = "$DEF_HTTPS_MIRROR" } else { warning KEYRING "Cannot check Release signature; keyring file not available %s" $1 if test -n $FORCE_KEYRING { error 1 KEYRING "Keyring-based check was requested; aborting accordingly" } } } } } ########################################################## variant handling proc doing_variant { if test $1 = $VARIANT { return 0; } if test $1 = "-" && test $VARIANT = "" { return 0; } return 1 } setvar SUPPORTED_VARIANTS = ""-"" proc variants { setvar SUPPORTED_VARIANTS = ""$ifsjoin(ARGV)"" for v in [$ifsjoin(ARGV)] { if doing_variant $v { return 0; } } error 1 UNSUPPVARIANT "unsupported variant" } ################################################# work out names for things proc mirror_style { match $1 { with release setvar DOWNLOAD_INDICES = 'download_release_indices' setvar DOWNLOAD_DEBS = 'download_release' with main setvar DOWNLOAD_INDICES = 'download_main_indices' setvar DOWNLOAD_DEBS = 'download_main' with * error 1 BADMIRROR "unknown mirror style" } export DOWNLOAD_INDICES export DOWNLOAD_DEBS } proc force_md5 { setvar DEBOOTSTRAP_CHECKSUM_FIELD = 'MD5SUM' export DEBOOTSTRAP_CHECKSUM_FIELD } proc verify_checksum { # args: dest checksum size local expchecksum="$2" local expsize="$3" if test $DEBOOTSTRAP_CHECKSUM_FIELD = "MD5SUM" { if in_path md5sum { setvar relchecksum = $(md5sum < "$1" | sed 's/ .*$//) } elif in_path md5 { setvar relchecksum = $(md5 ) } else { error 1 SIGCHECK "Cannot check md5sum" } } else { if in_path "sha${SHA_SIZE}sum" { setvar relchecksum = $(sha${SHA_SIZE}sum < "$1" | sed 's/ .*$//) } elif in_path "sha${SHA_SIZE}" { setvar relchecksum = $(sha${SHA_SIZE} ) } else { error 1 SIGCHECK "Cannot check sha${SHA_SIZE}sum" } } setvar relsize = $(wc -c ) if test $expsize -ne $relsize || test $expchecksum != $relchecksum { return 1 } return 0 } proc get { # args: from dest 'nocache' # args: from dest [checksum size] [alt {checksum size type}] local displayname local versionname if test ${2%.deb} != $2 { setvar displayname = "$(echo "$2" | sed 's,^.*/,,;s,_.*$,,')" setvar versionname = "$(echo "$2" | sed 's,^.*/,,' | cut -d_ -f2 | sed 's/%3a/:/')" } else { setvar displayname = "$(echo "$1" | sed 's,^.*/,,')" } if test -e $2 { if test -z $3 { return 0 } elif test $3 = nocache { rm -f $2 } else { info VALIDATING "Validating %s %s" $displayname $versionname if verify_checksum $2 $3 $4 { return 0 } else { rm -f $2 } } } # Drop 'nocache' option if test $3 = nocache { set $1 $2 } if test "$Argc" -gt 5 { local st=3 if test $5 = "-" { setvar st = '6'; } local order="$(a=$st; while [ "$a" -le $# ]; do eval echo \"\${$(($a+1))}\" $a; a=$(($a + 3)); done | sort -n | sed 's/.* //')" } else { local order=3 } for a in [$order] { local checksum="$(eval echo \${$a})" local siz="$(eval echo \${$(( $a+1 ))})" local typ="$(eval echo \${$(( $a+2 ))})" local from local dest local iters=0 match $typ { with xz setvar from = ""$1.xz""; setvar dest = ""$2.xz"" with bz2 setvar from = ""$1.bz2""; setvar dest = ""$2.bz2"" with gz setvar from = ""$1.gz""; setvar dest = ""$2.gz"" with * setvar from = "$1"; setvar dest = "$2" } if test ${dest#/} = $dest { setvar dest = ""./$dest"" } local dest2="$dest" if test -d "${dest2%/*}/partial" { setvar dest2 = ""${dest2%/*}/partial/${dest2##*/}"" } while test $iters -lt 10 { info RETRIEVING "Retrieving %s %s" $displayname $versionname if ! just_get $from $dest2 { continue 2; } if test $checksum != "" { info VALIDATING "Validating %s %s" $displayname $versionname if verify_checksum $dest2 $checksum $siz { setvar checksum = """" } } if test -z $checksum { test $dest2 = $dest || mv $dest2 $dest match $typ { with gz gunzip $dest with bz2 bunzip2 $dest with xz unxz $dest } return 0 } else { rm -f $dest2 warning RETRYING "Retrying failed download of %s" $from setvar iters = """$(($iters + 1))" } } warning CORRUPTFILE "%s was corrupt" $from } return 1 } proc just_get { # args: from dest local from="$1" local dest="$2" mkdir -p ${dest%/*} if test ${from#null:} != $from { error 1 NOTPREDL "%s was not pre-downloaded" ${from#null:} } elif test ${from#http://} != $from || test ${from#ftp://} != $from { # http/ftp mirror if wgetprogress -O $dest $from { return 0 } else { rm -f $dest return 1 } } elif test ${from#https://} != $from { # http/ftp mirror if wgetprogress $CHECKCERTIF $CERTIFICATE $PRIVATEKEY -O $dest $from { return 0 } else { rm -f $dest return 1 } } elif test ${from#file:} != $from { local base="${from#file:}" if test ${base#//} != $base { setvar base = ""/${from#file://*/}"" } if test -e $base { cp $base $dest return 0 } else { return 1 } } elif test ${from#ssh:} != $from { local ssh_dest="$(echo $from | sed -e 's#ssh://##' -e 's#/#:/#')" if test -n $ssh_dest { scp $ssh_dest $dest return 0 } else { return 1 } } else { error 1 UNKNOWNLOC "unknown location %s" $from } } proc download { mk_download_dirs $DOWNLOAD_DEBS $(echo "$@" | tr ' ' '\n' | sort) } proc download_indices { mk_download_dirs $DOWNLOAD_INDICES $(echo "$@" | tr ' ' '\n' | sort) } proc debfor { shell {while read pkg path { for p in [@ARGV] { test $p = $pkg || continue; echo $path } } <"$TARGET/debootstrap/debpaths" } } proc apt_dest { # args: # deb package version arch mirror path # pkg suite component arch mirror path # rel suite mirror path match $1 { with deb echo "/var/cache/apt/archives/${2}_${3}_${4}.deb" | sed 's/:/%3a/' with pkg local m="$5" setvar m = ""debootstrap.invalid"" #if [ "${m#http://}" != "$m" ]; then # m="${m#http://}" #elif [ "${m#file://}" != "$m" ]; then # m="file_localhost_${m#file://*/}" #elif [ "${m#file:/}" != "$m" ]; then # m="file_localhost_${m#file:/}" #fi printf "%s" "$APTSTATE/lists/" echo "${m}_$6" | sed 's/\//_/g' with rel local m="$3" setvar m = ""debootstrap.invalid"" #if [ "${m#http://}" != "$m" ]; then # m="${m#http://}" #elif [ "${m#file://}" != "$m" ]; then # m="file_localhost_${m#file://*/}" #elif [ "${m#file:/}" != "$m" ]; then # m="file_localhost_${m#file:/}" #fi printf "%s" "$APTSTATE/lists/" echo "${m}_$4" | sed 's/\//_/g' } } ################################################################## download proc get_release_checksum { local reldest="$1" local path="$2" if test $DEBOOTSTRAP_CHECKSUM_FIELD = MD5SUM { local match="^[Mm][Dd]5[Ss][Uu][Mm]" } else { local match="^[Ss][Hh][Aa]$SHA_SIZE:" } sed -n "/$match/,/^[^ ]/p" < "$reldest" | \ while read a b c { if test $c = $path { echo "$a $b"; } } | head -n 1 } proc extract_release_components { local reldest="$1"; shift setvar TMPCOMPONENTS = "$(sed -n 's/Components: *//p' "$reldest")" for c in [$TMPCOMPONENTS] { eval " case \"\$c\" in $USE_COMPONENTS) COMPONENTS=\"\$COMPONENTS \$c\" ;; esac " } setvar COMPONENTS = "$(echo $COMPONENTS)" if test -z $COMPONENTS { mv $reldest "$reldest.malformed" error 1 INVALIDREL "Invalid Release file, no valid components" } } setvar CODENAME = """" proc validate_suite { local reldest="$1" setvar CODENAME = $(sed -n "s/^Codename: *//p" "$reldest") local suite=$(sed -n "s/^Suite: *//p" "$reldest") if test $SUITE != $suite && test $SUITE != $CODENAME { error 1 WRONGSUITE "Asked to install suite %s, but got %s (codename: %s) from mirror" $SUITE $suite $CODENAME } } proc split_inline_sig { local inreldest="$1" local reldest="$2" local relsigdest="$3" # Note: InRelease files are fun since one needs to remove the # last newline from the PGP SIGNED MESSAGE part, while keeping # the PGP SIGNATURE part intact. This shell implementation # should work on most if not all systems, instead of trying to # sed/tr/head, etc. rm -f $reldest $relsigdest setvar nl = """" setvar state = 'pre-begin''' while IFS= read -r line { match ${state} { with pre-begin if test "x${line}" = "x-----BEGIN PGP SIGNED MESSAGE-----" { setvar state = 'begin' } with begin if test "x${line}" = "x" { setvar state = 'data' } with data if test "x${line}" = "x-----BEGIN PGP SIGNATURE-----" { printf "%s\n" ${line} > "$relsigdest" setvar state = 'signature' } else { printf "${nl}%s" ${line} >> "$reldest" setvar nl = ""\n"" } with signature printf "%s\n" ${line} >> "$relsigdest" if test "x${line}" = "x-----END PGP SIGNATURE-----" { break } } } < "$inreldest" } proc download_release_sig { local m1="$1" local inreldest="$2" local reldest="$3" local relsigdest="$4" progress 0 100 DOWNREL "Downloading Release file" progress_next 100 if get "$m1/dists/$SUITE/InRelease" $inreldest nocache { split_inline_sig $inreldest $reldest $relsigdest progress 100 100 DOWNREL "Downloading Release file" } else { get "$m1/dists/$SUITE/Release" $reldest nocache || error 1 NOGETREL "Failed getting release file %s" "$m1/dists/$SUITE/Release" progress 100 100 DOWNREL "Downloading Release file" } if test -n $KEYRING && test -z $DISABLE_KEYRING { progress 0 100 DOWNRELSIG "Downloading Release file signature" if ! test -f $relsigdest { progress_next 50 get "$m1/dists/$SUITE/Release.gpg" $relsigdest nocache || error 1 NOGETRELSIG "Failed getting release signature file %s" \ "$m1/dists/$SUITE/Release.gpg" progress 50 100 DOWNRELSIG "Downloading Release file signature" } info RELEASESIG "Checking Release signature" # Don't worry about the exit status from gpgv; parsing the output will # take care of that. shell {gpgv --status-fd 1 --keyring $KEYRING --ignore-time-conflict \ $relsigdest $reldest || true} | read_gpg_status progress 100 100 DOWNRELSIG "Downloading Release file signature" } } proc download_release_indices { local m1="${MIRRORS%% *}" local inreldest="$TARGET/$($DLDEST rel "$SUITE" "$m1" "dists/$SUITE/InRelease")" local reldest="$TARGET/$($DLDEST rel "$SUITE" "$m1" "dists/$SUITE/Release")" local relsigdest="$TARGET/$($DLDEST rel "$SUITE" "$m1" "dists/$SUITE/Release.gpg")" download_release_sig $m1 $inreldest $reldest $relsigdest validate_suite $reldest extract_release_components $reldest local totalpkgs=0 for c in [$COMPONENTS] { local subpath="$c/binary-$ARCH/Packages" local xzi="$(get_release_checksum $reldest "$subpath.xz)" local bz2i="$(get_release_checksum $reldest "$subpath.bz2)" local gzi="$(get_release_checksum $reldest "$subpath.gz)" local normi="$(get_release_checksum $reldest $subpath)" local i= if test $normi != "" { setvar i = "$normi" } elif in_path bunzip2 && test $bz2i != "" { setvar i = "$bz2i" } elif in_path unxz && test $xzi != "" { setvar i = "$xzi" } elif in_path gunzip && test $gzi != "" { setvar i = "$gzi" } if test $i != "" { setvar totalpkgs = """$(( $totalpkgs + ${i#* } ))" } else { mv $reldest "$reldest.malformed" error 1 MISSINGRELENTRY "Invalid Release file, no entry for %s" $subpath } } local donepkgs=0 local pkgdest progress 0 $totalpkgs DOWNPKGS "Downloading Packages files" for c in [$COMPONENTS] { local subpath="$c/binary-$ARCH/Packages" local path="dists/$SUITE/$subpath" local xzi="$(get_release_checksum $reldest "$subpath.xz)" local bz2i="$(get_release_checksum $reldest "$subpath.bz2)" local gzi="$(get_release_checksum $reldest "$subpath.gz)" local normi="$(get_release_checksum $reldest $subpath)" local ext= local i= if test $normi != "" { setvar ext = ""$ext $normi ."" setvar i = "$normi" } if in_path unxz && test $xzi != "" { setvar ext = ""$ext $xzi xz"" setvar i = "${i:-$xzi}" } if in_path bunzip2 && test $bz2i != "" { setvar ext = ""$ext $bz2i bz2"" setvar i = "${i:-$bz2i}" } if in_path gunzip && test $gzi != "" { setvar ext = ""$ext $gzi gz"" setvar i = "${i:-$gzi}" } progress_next "$(($donepkgs + ${i#* }))" for m in [$MIRRORS] { setvar pkgdest = ""$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m" "$path")"" if get "$m/$path" $pkgdest $ext { break; } } if test ! -f $pkgdest { error 1 COULDNTDL "Couldn't download %s" $path } setvar donepkgs = """$(($donepkgs + ${i#* }))" progress $donepkgs $totalpkgs DOWNPKGS "Downloading Packages files" } } proc get_package_sizes { # mirror pkgdest debs.. local m="$1"; shift local pkgdest="$1"; shift $PKGDETAILS PKGS $m $pkgdest @ARGV | shell { setvar newleft = """" setvar totaldebs = '0' setvar countdebs = '0' while read p details { if test $details = "-" { setvar newleft = ""$newleft $p"" } else { setvar size = "${details##* }"; setvar totaldebs = """$(($totaldebs + $size))" setvar countdebs = """$(($countdebs + 1))" } } echo "$countdebs $totaldebs$newleft" } } # note, leftovers come back on fd5 !! proc download_debs { local m="$1" local pkgdest="$2" shift; shift $PKGDETAILS PKGS $m $pkgdest @ARGV | shell { setvar leftover = """" while read p ver arc mdup fil checksum size { if test $ver = "-" { setvar leftover = ""$leftover $p"" } else { progress_next "$(($dloaddebs + $size))" local debdest="$($DLDEST deb "$p" "$ver" "$arc" "$m" "$fil")" if get "$m/$fil" "$TARGET/$debdest" $checksum $size { setvar dloaddebs = """$(($dloaddebs + $size))" echo >>$TARGET/debootstrap/deburis "$p $ver $m/$fil>>$TARGET/debootstrap/deburis "$p $ver $m/$fil" echo >>$TARGET/debootstrap/debpaths "$p $debdest>>$TARGET/debootstrap/debpaths "$p $debdest" } else { warning COULDNTDL "Couldn't download package %s (ver %s arch %s)" $p $ver $arc setvar leftover = ""$leftover $p"" } } } echo >&5 ${leftover# }>&5 ${leftover# } } } proc download_release { local m1="${MIRRORS%% *}" local numdebs="$Argc" local countdebs=0 progress $countdebs $numdebs SIZEDEBS "Finding package sizes" local totaldebs=0 local leftoverdebs="$ifsjoin(ARGV)" # Fix possible duplicate package names, which would screw up counts: setvar leftoverdebs = $(printf "$leftoverdebs"|tr ' ' '\n'|sort -u|tr '\n' ' ') setvar numdebs = $(printf "$leftoverdebs"|wc -w) for c in [$COMPONENTS] { if test $countdebs -ge $numdebs { break; } local path="dists/$SUITE/$c/binary-$ARCH/Packages" local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m1" "$path")" if test ! -e $pkgdest { continue; } info CHECKINGSIZES "Checking component %s on %s..." $c $m1 setvar leftoverdebs = "$(get_package_sizes "$m1" "$pkgdest" $leftoverdebs)" setvar countdebs = $(($countdebs + ${leftoverdebs%% *})) setvar leftoverdebs = ${leftoverdebs#* } setvar totaldebs = ${leftoverdebs%% *} setvar leftoverdebs = ${leftoverdebs#* } progress $countdebs $numdebs SIZEDEBS "Finding package sizes" } if test $countdebs -ne $numdebs { error 1 LEFTOVERDEBS "Couldn't find these debs: %s" $leftoverdebs } local dloaddebs=0 progress $dloaddebs $totaldebs DOWNDEBS "Downloading packages" :>$TARGET/debootstrap/debpaths setvar pkgs_to_get = ""$ifsjoin(ARGV)"" for c in [$COMPONENTS] { local path="dists/$SUITE/$c/binary-$ARCH/Packages" for m in [$MIRRORS] { local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m" "$path")" if test ! -e $pkgdest { continue; } setvar pkgs_to_get = "$(download_debs "$m" "$pkgdest" $pkgs_to_get 5>&1 1>&6)" if test -z $pkgs_to_get { break; } } 6>&1 if test -z $pkgs_to_get { break; } } progress $dloaddebs $totaldebs DOWNDEBS "Downloading packages" if test $pkgs_to_get != "" { error 1 COULDNTDLPKGS "Couldn't download packages: %s" $pkgs_to_get } } proc download_main_indices { local m1="${MIRRORS%% *}" local comp="${USE_COMPONENTS}" progress 0 100 DOWNMAINPKGS "Downloading Packages file" progress_next 100 if test -z $comp { setvar comp = 'main'; } setvar COMPONENTS = "$(echo $comp | tr '|' ' ')" export COMPONENTS for m in [$MIRRORS] { for c in [$COMPONENTS] { local path="dists/$SUITE/$c/binary-$ARCH/Packages" local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m" "$path")" if in_path gunzip && get "$m/${path}.gz" "${pkgdest}.gz" { rm -f $pkgdest gunzip "$pkgdest.gz" } elif get "$m/$path" $pkgdest { true } } } progress 100 100 DOWNMAINPKGS "Downloading Packages file" } proc download_main { local m1="${MIRRORS%% *}" :>$TARGET/debootstrap/debpaths for p in [@ARGV] { for c in [$COMPONENTS] { local details="" for m in [$MIRRORS] { local path="dists/$SUITE/$c/binary-$ARCH/Packages" local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m" "$path")" if test ! -e $pkgdest { continue; } setvar details = "$($PKGDETAILS PKGS "$m" "$pkgdest" "$p")" if test $details = "$p -" { setvar details = """" continue } setvar size = "${details##* }"; setvar details = "${details% *}" setvar checksum = "${details##* }"; setvar details = "${details% *}" local debdest="$($DLDEST deb $details)" if get "$m/${details##* }" "$TARGET/$debdest" $checksum $size { echo >>$TARGET/debootstrap/debpaths "$p $debdest>>$TARGET/debootstrap/debpaths "$p $debdest" setvar details = ""done"" break } } if test $details != "" { break } } if test $details != "done" { error 1 COULDNTDL "Couldn't download %s" $p } } } ###################################################### deb choosing support proc get_debs { local field="$1" shift local m1 c for m1 in [$MIRRORS] { for c in [$COMPONENTS] { local path="dists/$SUITE/$c/binary-$ARCH/Packages" local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m1" "$path")" echo $("$PKGDETAILS" FIELD "$field" "$m1" "$pkgdest" "$@" | sed 's/ .*//') } } } ################################################################ extraction setvar EXTRACTORS_SUPPORTED = ""dpkg-deb ar"" setvar EXTRACT_DEB_TAR_OPTIONS = '' # Native dpkg-deb based extractors proc extract_dpkg_deb_field { local pkg="$1" local field="$2" dpkg-deb -f $pkg $field } proc extract_dpkg_deb_data { local pkg="$1" dpkg-deb --fsys-tarfile $pkg | tar $EXTRACT_DEB_TAR_OPTIONS -xf - } # Raw .deb extractors proc extract_ar_deb_field { local pkg="$1" local field="$2" local tarball=$(ar -t "$pkg" | grep "^control\.tar") match $tarball { with control.tar.gz setvar cat_cmd = 'zcat' with control.tar.xz setvar cat_cmd = 'xzcat' with control.tar setvar cat_cmd = 'cat' with * error 1 UNKNOWNCONTROLCOMP "Unknown compression type for %s in %s" $tarball $pkg } if type $cat_cmd >/dev/null 2>&1 { ar -p $pkg $tarball | $cat_cmd | tar -O -xf - control ./control 2>/dev/null | grep -i "^$field:" | sed -e 's/[^:]*: *//' | head -n 1 } else { error 1 UNPACKCMDUNVL "Extracting %s requires the %s command, which is not available" $pkg $cat_cmd } } proc extract_ar_deb_data { local pkg="$1" local tarball=$(ar -t "$pkg" | grep "^data.tar") match $tarball { with data.tar.gz setvar cat_cmd = 'zcat' with data.tar.bz2 setvar cat_cmd = 'bzcat' with data.tar.xz setvar cat_cmd = 'xzcat' with data.tar setvar cat_cmd = 'cat' with * error 1 UNKNOWNDATACOMP "Unknown compression type for %s in %s" $tarball $pkg } if type $cat_cmd >/dev/null 2>&1 { ar -p $pkg $tarball | $cat_cmd | tar $EXTRACT_DEB_TAR_OPTIONS -xf - } else { error 1 UNPACKCMDUNVL "Extracting %s requires the %s command, which is not available" $pkg $cat_cmd } } proc valid_extractor { local extractor="$1" for E in [$EXTRACTORS_SUPPORTED] { if test $extractor = $E { return 0 } } return 1 } proc choose_extractor { local extractor if test -n $EXTRACTOR_OVERRIDE { setvar extractor = "$EXTRACTOR_OVERRIDE" } elif type dpkg-deb >/dev/null 2>&1 { setvar extractor = ""dpkg-deb"" } else { setvar extractor = ""ar"" } info CHOSENEXTRACTOR "Chosen extractor for .deb packages: %s" $extractor match $extractor { with dpkg-deb proc extract_deb_field { extract_dpkg_deb_field @ARGV; } proc extract_deb_data { extract_dpkg_deb_data @ARGV; } with ar proc extract_deb_field { extract_ar_deb_field @ARGV; } proc extract_deb_data { extract_ar_deb_data @ARGV; } } } proc extract { shell { cd $TARGET local p=0 cat_cmd for pkg in [$(debfor "$@")] { setvar p = """$(($p + 1))" progress $p "$Argc" EXTRACTPKGS "Extracting packages" setvar packagename = "$(echo "$pkg" | sed 's,^.*/,,;s,_.*$,,')" info EXTRACTING "Extracting %s..." $packagename extract_deb_data "./$pkg" } }; } proc in_target_nofail { if ! $CHROOT_CMD @ARGV 2>/dev/null { true } return 0 } proc in_target_failmsg { local code="$1" local msg="$2" local arg="$3" shift; shift; shift if ! $CHROOT_CMD @ARGV { warning $code $msg $arg # Try to point user at actual failing package. setvar msg = ""See %s for details"" if test -e "$TARGET/debootstrap/debootstrap.log" { setvar arg = ""$TARGET/debootstrap/debootstrap.log"" local pkg="$(grep '^dpkg: error processing ' "$TARGET/debootstrap/debootstrap.log" | head -n 1 | sed 's/\(error processing \)\(package \|archive \)/\1/' | cut -d ' ' -f 4)" if test -n $pkg { setvar msg = ""$msg (possibly the package $pkg is at fault)"" } } else { setvar arg = ""the log"" } warning $code $msg $arg return 1 } return 0 } proc in_target { in_target_failmsg IN_TARGET_FAIL "Failure trying to run: %s" "$CHROOT_CMD $ifsjoin(ARGV)" @ARGV } ###################################################### standard setup stuff proc conditional_cp { if test ! -e "$2/$1" { if test -L $1 && test -e $1 { cat $1 >"$2/$1" } elif test -e $1 { cp -a $1 "$2/$1" } } } proc mv_invalid_to { local m="$1" setvar m = "$(echo "${m#http://}" | tr '/' '_' | sed 's/_*//')" shell {cd "$TARGET/$APTSTATE/lists" for a in [debootstrap.invalid_*] { mv $a "${m}_${a#*_}" } } } proc setup_apt_sources { mkdir -p "$TARGET/etc/apt" for m in [@ARGV] { local cs="" for c in [${COMPONENTS:-$USE_COMPONENTS}] { local path="dists/$SUITE/$c/binary-$ARCH/Packages" local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m" "$path")" if test -e $pkgdest { setvar cs = ""$cs $c""; } } if test $cs != "" { echo "deb $m $SUITE$cs"; } } > "$TARGET/etc/apt/sources.list" } proc setup_etc { mkdir -p "$TARGET/etc" conditional_cp /etc/resolv.conf $TARGET conditional_cp /etc/hostname $TARGET if test $DLDEST = apt_dest && test ! -e "$TARGET/etc/apt/sources.list" { setup_apt_sources "http://debootstrap.invalid/" } } setvar UMOUNT_DIRS = '' proc umount_exit_function { local realdir for dir in [$UMOUNT_DIRS] { setvar realdir = "$(in_target_nofail readlink -f "$dir")" test $realdir || continue shell { cd / ; umount "$TARGET/${realdir#/}" } || true } } proc umount_on_exit { if test $UMOUNT_DIRS { setvar UMOUNT_DIRS = ""$UMOUNT_DIRS $1"" } else { setvar UMOUNT_DIRS = "$1" on_exit umount_exit_function } } proc clear_mtab { if test -f "$TARGET/etc/mtab" && test ! -h "$TARGET/etc/mtab" { rm -f "$TARGET/etc/mtab" } } proc setup_proc { match $HOST_OS { with *freebsd* umount_on_exit /dev umount_on_exit /proc umount "$TARGET/proc" 2>/dev/null || true if test $HOST_OS = kfreebsd { in_target mount -t linprocfs proc /proc } else { mount -t linprocfs proc $TARGET/proc } with hurd* # firmlink $TARGET/{dev,servers,proc} to the system ones. settrans -a "$TARGET/dev" /hurd/firmlink /dev settrans -a "$TARGET/servers" /hurd/firmlink /servers settrans -a "$TARGET/proc" /hurd/firmlink /proc with * umount_on_exit /dev/pts umount_on_exit /dev/shm umount_on_exit /proc/bus/usb umount_on_exit /proc umount "$TARGET/proc" 2>/dev/null || true in_target mount -t proc proc /proc if test -d "$TARGET/sys" && \ grep -q '[[:space:]]sysfs' /proc/filesystems 2>/dev/null { umount_on_exit /sys umount "$TARGET/sys" 2>/dev/null || true in_target mount -t sysfs sysfs /sys } on_exit clear_mtab } umount_on_exit /lib/init/rw } proc setup_proc_fakechroot { rm -rf "$TARGET/proc" ln -s /proc $TARGET } # create the static device nodes proc setup_devices { if doing_variant fakechroot { setup_devices_fakechroot return 0 } match $HOST_OS { with kfreebsd* with freebsd with hurd* with * setup_devices_simple } } # enable the dynamic device nodes proc setup_dynamic_devices { if doing_variant fakechroot { return 0 } match $HOST_OS { with kfreebsd* in_target mount -t devfs devfs /dev with freebsd mount -t devfs devfs $TARGET/dev with hurd* # Use the setup-translators of the hurd package in_target /usr/lib/hurd/setup-translators -k } } proc setup_devices_simple { # The list of devices that can be created in a container comes from # src/core/cgroup.c in the systemd source tree. mknod -m 666 $TARGET/dev/null c 1 3 mknod -m 666 $TARGET/dev/zero c 1 5 mknod -m 666 $TARGET/dev/full c 1 7 mknod -m 666 $TARGET/dev/random c 1 8 mknod -m 666 $TARGET/dev/urandom c 1 9 mknod -m 666 $TARGET/dev/tty c 5 0 mkdir $TARGET/dev/pts/ $TARGET/dev/shm/ # Inside a container, we might not be allowed to create /dev/ptmx. # If not, do the next best thing. if ! mknod -m 666 $TARGET/dev/ptmx c 5 2 { warning MKNOD "Could not create /dev/ptmx, falling back to symlink. This chroot will require /dev/pts mounted with ptmxmode=666" ln -s pts/ptmx $TARGET/dev/ptmx } ln -s /proc/self/fd $TARGET/dev/fd ln -s /proc/self/fd/0 $TARGET/dev/stdin ln -s /proc/self/fd/1 $TARGET/dev/stdout ln -s /proc/self/fd/2 $TARGET/dev/stderr } proc setup_devices_fakechroot { rm -rf "$TARGET/dev" ln -s /dev $TARGET } proc setup_dselect_method { match $1 { with apt mkdir -p "$TARGET/var/lib/dpkg" echo "apt apt" > "$TARGET/var/lib/dpkg/cmethopt" chmod 644 "$TARGET/var/lib/dpkg/cmethopt" with * error 1 UNKNOWNDSELECT "unknown dselect method" } } # Find out where the runtime dynamic linker and the shared libraries # can be installed on each architecture: native, multilib and multiarch. # This data can be verified by checking the files in the debian/sysdeps/ # directory of the glibc package. # # This function must be updated to support any new architecture which # either installs the RTLD in a directory different from /lib or builds # multilib library packages. proc setup_merged_usr { if test $MERGED_USR = "no" { return 0; } local link_dir match $ARCH { with hurd-* return 0 with amd64 setvar link_dir = ""lib32 lib64 libx32"" with i386 setvar link_dir = ""lib64 libx32"" with mips|mipsel setvar link_dir = ""lib32 lib64"" with mips64*|mipsn32* setvar link_dir = ""lib32 lib64 libo32"" with powerpc setvar link_dir = ""lib64"" with ppc64 setvar link_dir = ""lib32 lib64"" with ppc64el setvar link_dir = ""lib64"" with s390x setvar link_dir = ""lib32"" with sparc setvar link_dir = ""lib64"" with sparc64 setvar link_dir = ""lib32 lib64"" with x32 setvar link_dir = ""lib32 lib64 libx32"" } setvar link_dir = ""bin sbin lib $link_dir"" local dir for dir in [$link_dir] { ln -s usr/$dir $TARGET/$dir mkdir -p $TARGET/usr/$dir } } ################################################################ pkgdetails # NOTE # For the debootstrap udeb, pkgdetails is provided by the bootstrap-base # udeb, so the pkgdetails API needs to be kept in sync with that. if in_path perl { setvar PKGDETAILS = 'pkgdetails_perl' proc pkgdetails_field { # uniq field mirror Packages values... perl -le ' $unique = shift @ARGV; $field = lc(shift @ARGV); $mirror = shift @ARGV; %fields = map { $_, 0 } @ARGV; $prevpkg = ""; while () { chomp; next if (/^ /); if (/^([^:]*:)\s*(.*)$/) { $f = lc($1); $v = $2; if ($f eq "package:") { $last = 0; $pkg = $v; if ($pkg ne $prevpkg) { print $output if defined $output; if ($unique && defined $output_val) { delete $fields{$output_val}; $last = 1 unless keys %fields; } $prevpkg = $pkg; } undef $output; undef $output_val; last if $last; } $ver = $v if ($f eq "version:"); $arc = $v if ($f eq "architecture:"); $fil = $v if ($f eq "filename:"); $chk = $v if (lc $f eq lc($ENV{DEBOOTSTRAP_CHECKSUM_FIELD}).":"); $siz = $v if ($f eq "size:"); $val = $v if ($f eq $field); } elsif (/^$/) { if (defined $val && defined $fields{$val}) { $output = sprintf "%s %s %s %s %s %s %s", $pkg, $ver, $arc, $mirror, $fil, $chk, $siz; $output_val = $val; } undef $val; } } print $output if defined $output; delete $fields{$output_val} if $unique && defined $output_val; for $v (keys %fields) { printf ("%s -\n", $v) if ($unique); } ' @ARGV } proc pkgdetails_perl { if test $1 = "WGET%" { shift; perl -e ' $v = 0; $allow_percentage = 0; while (read STDIN, $x, 1) { if ($x =~ m/\s/) { $allow_percentage = 1; } elsif ($allow_percentage and $x =~ m/\d/) { $v *= 10; $v += $x; } elsif ($allow_percentage and $x eq "%") { printf "P: %d %d%s\n", int($v / 100.0 * ($ARGV[1] - $ARGV[0]) + $ARGV[0]), $ARGV[2], ($#ARGV == 3 ? " $ARGV[3]" : ""); $v = 0; } else { $v = 0; $allow_percentage = 0; } }' @ARGV } elif test $1 = "GETDEPS" { local pkgdest="$2"; shift; shift perl -e ' $prevpkg = ""; @d = (); while () { chomp; if (/^Package: (.*)$/) { $pkg = $1; if ($pkg ne $prevpkg) { for my $d (@d) { print "$d\n"; } } $prevpkg = $1; @d = (); } $in = 1 if (grep {$_ eq $pkg} @ARGV); $in = 0 if (/^$/); if ($in and (/^Depends: (.*)$/ or /^Pre-Depends: (.*)$/)) { for $d (split /\s*,\s*/, $1) { $d =~ s/\s*[|].*$//; $d =~ s/\s*[(].*[)]\s*//; $d =~ s/:.*//; push @d, $d; } } } for my $d (@d) { print "$d\n"; }' <"$pkgdest" @ARGV<"$pkgdest" "$@" | sort | uniq } elif test $1 = "PKGS" { local m="$2" local p="$3" shift; shift; shift pkgdetails_field 1 Package: $m @ARGV < "$p" } elif test $1 = "FIELD" { local f="$2" local m="$3" local p="$4" shift; shift; shift; shift pkgdetails_field 0 $f $m @ARGV < "$p" } elif test $1 = "STANZAS" { local pkgdest="$2"; shift; shift perl -e ' my $accum = ""; while () { $accum .= $_; $in = 1 if (/^Package: (.*)$/ && grep {$_ eq $1} @ARGV); if ($in and /^$/) { print $accum; if (substr($accum, -1) != "\n") { print "\n\n"; } elsif (substr($accum, -2, 1) != "\n") { print "\n"; } $in = 0; } $accum = "" if /^$/; }' <"$pkgdest" @ARGV<"$pkgdest" "$@" } } } elif test -e "/usr/lib/debootstrap/pkgdetails" { setvar PKGDETAILS = ""/usr/lib/debootstrap/pkgdetails"" } elif test -e "$DEBOOTSTRAP_DIR/pkgdetails" { setvar PKGDETAILS = ""$DEBOOTSTRAP_DIR/pkgdetails"" } else { setvar PKGDETAILS = """" } ##################################################### dependency resolution proc resolve_deps { local m1="${MIRRORS%% *}" local PKGS="$ifsjoin(ARGV)" local ALLPKGS="$PKGS"; local ALLPKGS2=""; while test $PKGS != "" { local NEWPKGS="" for c in [${COMPONENTS:-$USE_COMPONENTS}] { local path="dists/$SUITE/$c/binary-$ARCH/Packages" local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m1" "$path")" setvar NEWPKGS = ""$NEWPKGS $("$PKGDETAILS" GETDEPS "$pkgdest" $PKGS)"" } setvar PKGS = $(echo "$PKGS $NEWPKGS" | tr ' ' '\n' | sort | uniq) local REALPKGS="" for c in [${COMPONENTS:-$USE_COMPONENTS}] { local path="dists/$SUITE/$c/binary-$ARCH/Packages" local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m1" "$path")" setvar REALPKGS = ""$REALPKGS $("$PKGDETAILS" PKGS REAL "$pkgdest" $PKGS | sed -n 's/ .*REAL.*$//p')"" } setvar PKGS = "$REALPKGS" setvar ALLPKGS2 = $(echo "$PKGS $ALLPKGS" | tr ' ' '\n' | sort | uniq) setvar PKGS = $(without "$ALLPKGS2" "$ALLPKGS") setvar ALLPKGS = "$ALLPKGS2" } echo $ALLPKGS } proc setup_available { local m1="${MIRRORS%% *}" for c in [${COMPONENTS:-$USE_COMPONENTS}] { local path="dists/$SUITE/$c/binary-$ARCH/Packages" local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m1" "$path")" # XXX: What if a package is in more than one component? # -- cjwatson 2009-07-29 $PKGDETAILS STANZAS $pkgdest @ARGV }for pkg in @ARGV { echo "$pkg install" } | in_target dpkg --set-selections } proc get_next_predep { local stanza="$(in_target_nofail dpkg --predep-package)" test $stanza || return 1 echo $stanza | grep '^Package:' | sed 's/^Package://; s/^ *//' } ################################################################### helpers # Return zero if it is possible to create devices and execute programs in # this directory. (Both may be forbidden by mount options, e.g. nodev and # noexec respectively.) proc check_sane_mount { mkdir -p $1 match $HOST_OS { with *freebsd*|hurd* with * mknod "$1/test-dev-null" c 1 3 || return 1 if ! echo test > "$1/test-dev-null" { rm -f "$1/test-dev-null" return 1 } rm -f "$1/test-dev-null" } setvar SH = '/bin/sh' test -x $SH || setvar SH = $(which sh) cat > "$1/test-exec" <<< """ #! $SH : """ chmod +x "$1/test-exec" if ! "$1/test-exec" { rm -f "$1/test-exec" return 1 } rm -f "$1/test-exec" return 0 } proc read_gpg_status { setvar badsig = '' setvar unkkey = '' setvar validsig = '' while read prefix keyword keyid rest { test $prefix = '[GNUPG:]' || continue match $keyword { with BADSIG setvar badsig = "$keyid" with NO_PUBKEY setvar unkkey = "$keyid" with VALIDSIG setvar validsig = "$keyid" } } if test $validsig { info VALIDRELSIG "Valid Release signature (key id %s)" $validsig } elif test $badsig { error 1 BADRELSIG "Invalid Release signature (key id %s)" $badsig } elif test $unkkey { error 1 UNKNOWNRELSIG "Release signed by unknown key (key id %s)" $unkkey } else { error 1 SIGCHECK "Error executing gpgv to check Release signature" } } proc without { # usage: without "a b c" "a d" -> "b" "c" shell {echo $1 | tr ' ' '\n' | sort | uniq; echo $2 $2 | tr ' ' '\n'} | sort | uniq -u | tr '\n' ' ' echo } # Formerly called 'repeat', but that's a reserved word in zsh. proc repeatn { local n="$1" shift while test $n -gt 0 { if @ARGV { break } else { setvar n = """$(( $n - 1 ))" sleep 1 } } if test $n -eq 0 { return 1; } return 0 } setvar N_EXIT_THINGS = '0' proc exit_function { local n=0 while test $n -lt $N_EXIT_THINGS { shell {eval $(eval echo \${EXIT_THING_$n}) 2>/dev/null || true} setvar n = """$(( $n + 1 ))" } setvar N_EXIT_THINGS = '0' } trap "exit_function" 0 trap "exit 129" 1 trap "error 130 INTERRUPTED \"Interrupt caught ... exiting\"" 2 trap "exit 131" 3 trap "exit 143" 15 proc on_exit { eval $(echo EXIT_THING_${N_EXIT_THINGS}='"'$1'"') setvar N_EXIT_THINGS = """$(( $N_EXIT_THINGS + 1 ))" } ############################################################## fakechroot tools proc install_fakechroot_tools { if test $VARIANT = "fakechroot" { export PATH=/usr/sbin:/sbin:$PATH } mv "$TARGET/sbin/ldconfig" "$TARGET/sbin/ldconfig.REAL" echo \ "#!/bin/sh echo echo \"Warning: Fake ldconfig called, doing nothing\"" > "$TARGET/sbin/ldconfig" chmod 755 "$TARGET/sbin/ldconfig" echo \ "/sbin/ldconfig /sbin/ldconfig.REAL fakechroot" >> "$TARGET/var/lib/dpkg/diversions" mv "$TARGET/usr/bin/ldd" "$TARGET/usr/bin/ldd.REAL" cat <<< ''' > "$TARGET/usr/bin/ldd" #!/usr/bin/perl # fakeldd # # Replacement for ldd with usage of objdump # # (c) 2003-2005 Piotr Roszatycki , BSD my %libs = (); my $status = 0; my $dynamic = 0; my $biarch = 0; my $ldlinuxsodir = "/lib"; my @ld_library_path = qw(/usr/lib /lib); sub ldso($) { my ($lib) = @_; my @files = (); if ($lib =~ /^\//) { $libs{$lib} = $lib; push @files, $lib; } else { foreach my $ld_path (@ld_library_path) { next unless -f "$ld_path/$lib"; my $badformat = 0; open OBJDUMP, "objdump -p $ld_path/$lib 2>/dev/null |"; while (my $line = ) { if ($line =~ /file format (\S*)$/) { $badformat = 1 unless $format eq $1; last; } } close OBJDUMP; next if $badformat; $libs{$lib} = "$ld_path/$lib"; push @files, "$ld_path/$lib"; } objdump(@files); } } sub objdump(@) { my (@files) = @_; my @libs = (); foreach my $file (@files) { open OBJDUMP, "objdump -p $file 2>/dev/null |"; while (my $line = ) { $line =~ s/^\s+//; my @f = split (/\s+/, $line); if ($line =~ /file format (\S*)$/) { if (not $format) { $format = $1; if ($unamearch eq "x86_64" and $format eq "elf32-i386") { my $link = readlink "/lib/ld-linux.so.2"; if ($link =~ /^\/emul\/ia32-linux\//) { $ld_library_path[-2] = "/emul/ia32-linux/usr/lib"; $ld_library_path[-1] = "/emul/ia32-linux/lib"; } } elsif ($unamearch =~ /^(sparc|sparc64)$/ and $format eq "elf64-sparc") { $ldlinuxsodir = "/lib64"; $ld_library_path[-2] = "/usr/lib64"; $ld_library_path[-1] = "/lib64"; } } else { next unless $format eq $1; } } if (not $dynamic and $f[0] eq "Dynamic") { $dynamic = 1; } next unless $f[0] eq "NEEDED"; if ($f[1] =~ /^ld-linux(\.|-)/) { $f[1] = "$ldlinuxsodir/" . $f[1]; } if (not defined $libs{$f[1]}) { $libs{$f[1]} = undef; push @libs, $f[1]; } } close OBJDUMP; } foreach my $lib (@libs) { ldso($lib); } } if ($#ARGV < 0) { print STDERR "fakeldd: missing file arguments\n"; exit 1; } while ($ARGV[0] =~ /^-/) { my $arg = $ARGV[0]; shift @ARGV; last if $arg eq "--"; } open LD_SO_CONF, "/etc/ld.so.conf"; while ($line = ) { chomp $line; unshift @ld_library_path, $line; } close LD_SO_CONF; unshift @ld_library_path, split(/:/, $ENV{LD_LIBRARY_PATH}); $unamearch = `/bin/uname -m`; chomp $unamearch; foreach my $file (@ARGV) { my $address; %libs = (); $dynamic = 0; if ($#ARGV > 0) { print "$file:\n"; } if (not -f $file) { print STDERR "ldd: $file: No such file or directory\n"; $status = 1; next; } objdump($file); if ($dynamic == 0) { print "\tnot a dynamic executable\n"; $status = 1; } elsif (scalar %libs eq "0") { print "\tstatically linked\n"; } if ($format =~ /^elf64-/) { $address = "0x0000000000000000"; } else { $address = "0x00000000"; } foreach $lib (keys %libs) { if ($libs{$lib}) { printf "\t%s => %s (%s)\n", $lib, $libs{$lib}, $address; } else { printf "\t%s => not found\n", $lib; } } } exit $status; ''' > "$TARGET/usr/bin/ldd" #!/usr/bin/perl # fakeldd # # Replacement for ldd with usage of objdump # # (c) 2003-2005 Piotr Roszatycki , BSD my %libs = (); my $status = 0; my $dynamic = 0; my $biarch = 0; my $ldlinuxsodir = "/lib"; my @ld_library_path = qw(/usr/lib /lib); sub ldso($) { my ($lib) = @_; my @files = (); if ($lib =~ /^\//) { $libs{$lib} = $lib; push @files, $lib; } else { foreach my $ld_path (@ld_library_path) { next unless -f "$ld_path/$lib"; my $badformat = 0; open OBJDUMP, "objdump -p $ld_path/$lib 2>/dev/null |"; while (my $line = ) { if ($line =~ /file format (\S*)$/) { $badformat = 1 unless $format eq $1; last; } } close OBJDUMP; next if $badformat; $libs{$lib} = "$ld_path/$lib"; push @files, "$ld_path/$lib"; } objdump(@files); } } sub objdump(@) { my (@files) = @_; my @libs = (); foreach my $file (@files) { open OBJDUMP, "objdump -p $file 2>/dev/null |"; while (my $line = ) { $line =~ s/^\s+//; my @f = split (/\s+/, $line); if ($line =~ /file format (\S*)$/) { if (not $format) { $format = $1; if ($unamearch eq "x86_64" and $format eq "elf32-i386") { my $link = readlink "/lib/ld-linux.so.2"; if ($link =~ /^\/emul\/ia32-linux\//) { $ld_library_path[-2] = "/emul/ia32-linux/usr/lib"; $ld_library_path[-1] = "/emul/ia32-linux/lib"; } } elsif ($unamearch =~ /^(sparc|sparc64)$/ and $format eq "elf64-sparc") { $ldlinuxsodir = "/lib64"; $ld_library_path[-2] = "/usr/lib64"; $ld_library_path[-1] = "/lib64"; } } else { next unless $format eq $1; } } if (not $dynamic and $f[0] eq "Dynamic") { $dynamic = 1; } next unless $f[0] eq "NEEDED"; if ($f[1] =~ /^ld-linux(\.|-)/) { $f[1] = "$ldlinuxsodir/" . $f[1]; } if (not defined $libs{$f[1]}) { $libs{$f[1]} = undef; push @libs, $f[1]; } } close OBJDUMP; } foreach my $lib (@libs) { ldso($lib); } } if ($#ARGV < 0) { print STDERR "fakeldd: missing file arguments\n"; exit 1; } while ($ARGV[0] =~ /^-/) { my $arg = $ARGV[0]; shift @ARGV; last if $arg eq "--"; } open LD_SO_CONF, "/etc/ld.so.conf"; while ($line = ) { chomp $line; unshift @ld_library_path, $line; } close LD_SO_CONF; unshift @ld_library_path, split(/:/, $ENV{LD_LIBRARY_PATH}); $unamearch = `/bin/uname -m`; chomp $unamearch; foreach my $file (@ARGV) { my $address; %libs = (); $dynamic = 0; if ($#ARGV > 0) { print "$file:\n"; } if (not -f $file) { print STDERR "ldd: $file: No such file or directory\n"; $status = 1; next; } objdump($file); if ($dynamic == 0) { print "\tnot a dynamic executable\n"; $status = 1; } elsif (scalar %libs eq "0") { print "\tstatically linked\n"; } if ($format =~ /^elf64-/) { $address = "0x0000000000000000"; } else { $address = "0x00000000"; } foreach $lib (keys %libs) { if ($libs{$lib}) { printf "\t%s => %s (%s)\n", $lib, $libs{$lib}, $address; } else { printf "\t%s => not found\n", $lib; } } } exit $status; END chmod 755 "$TARGET/usr/bin/ldd" echo \ "/usr/bin/ldd /usr/bin/ldd.REAL fakechroot" >> "$TARGET/var/lib/dpkg/diversions" }