############################################################### smallutils proc smallyes { setglobal YES = $(1-y) while echo $YES !2 >/dev/null { : ; } } proc in_path { local OLD_IFS="$IFS" setglobal IFS = '":'" for dir in [$PATH] { if test -x "$dir/$1" { setglobal IFS = $OLD_IFS return 0 } } setglobal 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 } } setglobal PROGRESS_NOW = '0' setglobal PROGRESS_END = '0' setglobal PROGRESS_NEXT = ''"" setglobal PROGRESS_WHAT = ''"" proc progress_next { setglobal PROGRESS_NEXT = $1 } proc wgetprogress { test ! $VERBOSE && setglobal 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 setglobal ret = $Status } else { wget $QSWITCH @Argv setglobal 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 { setglobal PROGRESS_NOW = $now setglobal PROGRESS_END = $end setglobal 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 { setglobal expect = 'half-installed' } elif test $action = CONFIGURING { setglobal expect = 'half-configured' } proc dp { setglobal now = "$shExpr('$now + ${1:-1}')" } setglobal exitcode = '0' while read status pkg qstate { if test $status = "EXITCODE" { setglobal exitcode = $pkg continue } test $qstate = $expect || continue match $qstate { with half-installed dp; progress $now $end $name $desc info $action "Unpacking %s..." $(pkg%:) setglobal expect = 'unpacked' with unpacked setglobal expect = 'half-installed' with half-configured dp; progress $now $end $name $desc info $action "Configuring %s..." $(pkg%:) setglobal expect = 'installed' with installed setglobal expect = 'half-configured' } } return $exitcode } ############################################################# set variables proc default_mirror { setglobal DEF_MIRROR = $1 } setglobal FINDDEBS_NEEDS_INDICES = 'false' proc finddebs_style { match $1 { with hardcoded with from-indices setglobal 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" { setglobal APTSTATE = 'var/state/apt' } else { setglobal APTSTATE = 'var/lib/apt' } setglobal DLDEST = 'apt_dest' export APTSTATE DLDEST DEBFOR with * error 1 BADDLOAD "unknown download style" } } proc keyring { if test -z $KEYRING { if test -e $1 { setglobal 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 setglobal 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 } setglobal SUPPORTED_VARIANTS = '"-'" proc variants { setglobal 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 setglobal DOWNLOAD_INDICES = 'download_release_indices' setglobal DOWNLOAD_DEBS = 'download_release' with main setglobal DOWNLOAD_INDICES = 'download_main_indices' setglobal DOWNLOAD_DEBS = 'download_main' with * error 1 BADMIRROR "unknown mirror style" } export DOWNLOAD_INDICES export DOWNLOAD_DEBS } proc force_md5 { setglobal 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 { setglobal relchecksum = $[md5sum < $1 | sed 's/ .*$//] } elif in_path md5 { setglobal relchecksum = $[md5 < $1] } else { error 1 SIGCHECK "Cannot check md5sum" } } else { if in_path "sha$(SHA_SIZE)sum" { setglobal relchecksum = $[sha$(SHA_SIZE)sum < $1 | sed 's/ .*$//] } elif in_path "sha$(SHA_SIZE)" { setglobal relchecksum = $[sha$(SHA_SIZE) < $1] } else { error 1 SIGCHECK "Cannot check sha$(SHA_SIZE)sum" } } setglobal relsize = $[wc -c < $1] 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 { setglobal displayname = $[echo $2 | sed 's,^.*/,,;s,_.*$,,] setglobal versionname = $[echo $2 | sed 's,^.*/,,' | cut -d_ -f2 | sed 's/%3a/:/] } else { setglobal 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 = "-" { setglobal st = '6'; } local order="$[setglobal a = $st; while test $a -le $Argc { eval echo '"''$'{$shExpr('$a+1')}'"' $a; setglobal a = $shExpr('$a + 3'); } | sort -n | sed 's/.* //]" } else { local order=3 } for a in [$order] { local checksum="$[eval echo '$'{$a}]" local siz="$[eval echo '$'{$shExpr(' $a+1 ')}]" local typ="$[eval echo '$'{$shExpr(' $a+2 ')}]" local from local dest local iters=0 match $typ { with xz setglobal from = ""$1.xz""; setglobal dest = ""$2.xz"" with bz2 setglobal from = ""$1.bz2""; setglobal dest = ""$2.bz2"" with gz setglobal from = ""$1.gz""; setglobal dest = ""$2.gz"" with * setglobal from = $1; setglobal dest = $2 } if test $(dest#/) = $dest { setglobal dest = ""./$dest"" } local dest2="$dest" if test -d "$(dest2%/*)/partial" { setglobal 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 { setglobal 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 setglobal iters = "$shExpr('$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 { setglobal 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 @Argv | tr ' ' '\n' | sort] } proc download_indices { mk_download_dirs $DOWNLOAD_INDICES $[echo @Argv | 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" setglobal 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" setglobal 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 setglobal TMPCOMPONENTS = $[sed -n 's/Components: *//p' $reldest] for c in [$TMPCOMPONENTS] { eval " case \"\$c\" in $USE_COMPONENTS) COMPONENTS=\"\$COMPONENTS \$c\" ;; esac " } setglobal COMPONENTS = $[echo $COMPONENTS] if test -z $COMPONENTS { mv $reldest "$reldest.malformed" error 1 INVALIDREL "Invalid Release file, no valid components" } } setglobal CODENAME = ''"" proc validate_suite { local reldest="$1" setglobal 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 setglobal nl = ''"" setglobal state = 'pre-begin' while env IFS= read -r line { match $(state) { with pre-begin if test "x$(line)" = "x-----BEGIN PGP SIGNED MESSAGE-----" { setglobal state = 'begin' } with begin if test "x$(line)" = "x" { setglobal state = 'data' } with data if test "x$(line)" = "x-----BEGIN PGP SIGNATURE-----" { printf "%s\n" $(line) > $relsigdest setglobal state = 'signature' } else { printf "$(nl)%s" $(line) >> $reldest setglobal 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 != "" { setglobal i = $normi } elif in_path bunzip2 && test $bz2i != "" { setglobal i = $bz2i } elif in_path unxz && test $xzi != "" { setglobal i = $xzi } elif in_path gunzip && test $gzi != "" { setglobal i = $gzi } if test $i != "" { setglobal totalpkgs = "$shExpr(' $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 != "" { setglobal ext = ""$ext $normi ."" setglobal i = $normi } if in_path unxz && test $xzi != "" { setglobal ext = ""$ext $xzi xz"" setglobal i = $(i:-$xzi) } if in_path bunzip2 && test $bz2i != "" { setglobal ext = ""$ext $bz2i bz2"" setglobal i = $(i:-$bz2i) } if in_path gunzip && test $gzi != "" { setglobal ext = ""$ext $gzi gz"" setglobal i = $(i:-$gzi) } progress_next "$shExpr('$donepkgs + ${i#* }')" for m in [$MIRRORS] { setglobal 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 } setglobal donepkgs = "$shExpr('$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 { setglobal newleft = ''"" setglobal totaldebs = '0' setglobal countdebs = '0' while read p details { if test $details = "-" { setglobal newleft = ""$newleft $p"" } else { setglobal size = $(details##* ); setglobal totaldebs = "$shExpr('$totaldebs + $size')" setglobal countdebs = "$shExpr('$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 { setglobal leftover = ''"" while read p ver arc mdup fil checksum size { if test $ver = "-" { setglobal leftover = ""$leftover $p"" } else { progress_next "$shExpr('$dloaddebs + $size')" local debdest="$[$DLDEST deb $p $ver $arc $m $fil]" if get "$m/$fil" "$TARGET/$debdest" $checksum $size { setglobal dloaddebs = "$shExpr('$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 setglobal 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: setglobal leftoverdebs = $[printf $leftoverdebs|tr ' ' '\n'|sort -u|tr '\n' ' ] setglobal 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 setglobal leftoverdebs = $[get_package_sizes $m1 $pkgdest $leftoverdebs] setglobal countdebs = $shExpr('$countdebs + ${leftoverdebs%% *}') setglobal leftoverdebs = $(leftoverdebs#* ) setglobal totaldebs = $(leftoverdebs%% *) setglobal 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 setglobal 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; } setglobal 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 { setglobal comp = 'main'; } setglobal 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; } setglobal details = $[$PKGDETAILS PKGS $m $pkgdest $p] if test $details = "$p -" { setglobal details = ''"" continue } setglobal size = $(details##* ); setglobal details = $(details% *) setglobal checksum = $(details##* ); setglobal 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" setglobal 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 @Argv | sed 's/ .*//] } } } ################################################################ extraction setglobal EXTRACTORS_SUPPORTED = '"dpkg-deb ar'" setglobal 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 setglobal cat_cmd = 'zcat' with control.tar.xz setglobal cat_cmd = 'xzcat' with control.tar setglobal 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 setglobal cat_cmd = 'zcat' with data.tar.bz2 setglobal cat_cmd = 'bzcat' with data.tar.xz setglobal cat_cmd = 'xzcat' with data.tar setglobal 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 { setglobal extractor = $EXTRACTOR_OVERRIDE } elif type dpkg-deb >/dev/null !2 > !1 { setglobal extractor = '"dpkg-deb'" } else { setglobal 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 @Argv]] { setglobal p = "$shExpr('$p + 1')" progress $p "$Argc" EXTRACTPKGS "Extracting packages" setglobal 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. setglobal msg = '"See %s for details'" if test -e "$TARGET/debootstrap/debootstrap.log" { setglobal 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 { setglobal msg = ""$msg (possibly the package $pkg is at fault)"" } } else { setglobal 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" setglobal 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 { setglobal 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/" } } setglobal UMOUNT_DIRS = '' proc umount_exit_function { local realdir for dir in [$UMOUNT_DIRS] { setglobal realdir = $[in_target_nofail readlink -f $dir] test $realdir || continue shell { cd / ; umount "$TARGET/$(realdir#/)" } || true } } proc umount_on_exit { if test $UMOUNT_DIRS { setglobal UMOUNT_DIRS = ""$UMOUNT_DIRS $1"" } else { setglobal 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 setglobal link_dir = '"lib32 lib64 libx32'" with i386 setglobal link_dir = '"lib64 libx32'" with mips|mipsel setglobal link_dir = '"lib32 lib64'" with mips64*|mipsn32* setglobal link_dir = '"lib32 lib64 libo32'" with powerpc setglobal link_dir = '"lib64'" with ppc64 setglobal link_dir = '"lib32 lib64'" with ppc64el setglobal link_dir = '"lib64'" with s390x setglobal link_dir = '"lib32'" with sparc setglobal link_dir = '"lib64'" with sparc64 setglobal link_dir = '"lib32 lib64'" with x32 setglobal link_dir = '"lib32 lib64 libx32'" } setglobal 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 { setglobal 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" { setglobal PKGDETAILS = '"/usr/lib/debootstrap/pkgdetails'" } elif test -e "$DEBOOTSTRAP_DIR/pkgdetails" { setglobal PKGDETAILS = ""$DEBOOTSTRAP_DIR/pkgdetails"" } else { setglobal 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]" setglobal NEWPKGS = ""$NEWPKGS $[$PKGDETAILS GETDEPS $pkgdest $PKGS]"" } setglobal 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]" setglobal REALPKGS = ""$REALPKGS $[$PKGDETAILS PKGS REAL $pkgdest $PKGS | sed -n 's/ .*REAL.*$//p]"" } setglobal PKGS = $REALPKGS setglobal ALLPKGS2 = $[echo "$PKGS $ALLPKGS" | tr ' ' '\n' | sort | uniq] setglobal PKGS = $[without $ALLPKGS2 $ALLPKGS] setglobal 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" } setglobal SH = '/bin/sh' test -x $SH || setglobal 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 { setglobal badsig = '' setglobal unkkey = '' setglobal validsig = '' while read prefix keyword keyid rest { test $prefix = '[GNUPG:]' || continue match $keyword { with BADSIG setglobal badsig = $keyid with NO_PUBKEY setglobal unkkey = $keyid with VALIDSIG setglobal 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 { setglobal n = "$shExpr(' $n - 1 ')" sleep 1 } } if test $n -eq 0 { return 1; } return 0 } setglobal 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} setglobal n = "$shExpr(' $n + 1 ')" } setglobal 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'"'] setglobal N_EXIT_THINGS = "$shExpr(' $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" }