diff --git a/.gitignore b/.gitignore index 09387eb..bb4e798 100644 --- a/.gitignore +++ b/.gitignore @@ -330,3 +330,4 @@ setup_logs/ /research/xnu /vm /vm.backups +/debs/ diff --git a/debs.list b/debs.list new file mode 100644 index 0000000..c6e8f3e --- /dev/null +++ b/debs.list @@ -0,0 +1,17 @@ +# debs.list — URLs of .deb packages to download during machine setup (JB/EXP). +# +# One URL per line. Blank lines and lines starting with '#' are ignored. +# +# Downloads are cached in ./debs/ (keyed by filename); a file already present +# there is not re-downloaded. You may also drop .deb files into ./debs/ +# manually — they are installed on first boot alongside the downloaded ones. +# +# A failed download prints an error and is skipped; it never aborts setup. +# Packages are installed with `dpkg -i` on first boot (same as Sileo); this +# does not resolve dependencies. +# +# Example: +# https://havoc.app/get/com.example.pkg/com.example.pkg_1.0_iphoneos-arm64.deb +https://apt.procurs.us/pool/main/iphoneos-arm64-rootless/3000/openssh/openssh-client_9.7p1-1_iphoneos-arm64.deb +https://apt.procurs.us/pool/main/iphoneos-arm64-rootless/3000/openssh/openssh-sftp-server_9.7p1-1_iphoneos-arm64.deb +https://apt.procurs.us/pool/main/iphoneos-arm64-rootless/3000/openssh/openssh-server_9.7p1-1_iphoneos-arm64.deb diff --git a/scripts/cfw_install_exp.sh b/scripts/cfw_install_exp.sh index c5da8f7..982db4b 100755 --- a/scripts/cfw_install_exp.sh +++ b/scripts/cfw_install_exp.sh @@ -473,6 +473,24 @@ if [[ -f "$SILEO_DEB" ]]; then cp -R "$SILEO_DEB" "$MNT5/$BOOT_HASH/org.coolstar.sileo_2.5.1_iphoneos-arm64.deb" fi +# ── Extra debs: download from manifest, then stage the whole cache ────── +echo " Fetching extra debs..." +zsh "$SCRIPT_DIR/fetch_debs.sh" || true +DEBS_CACHE="${SCRIPT_DIR:h}/debs" +DEBS_DEST="$MNT5/$BOOT_HASH/debs" +/bin/rm -rf "$DEBS_DEST" +deb_count=0 +for deb in "$DEBS_CACHE"/*.deb(N); do + (( deb_count == 0 )) && /bin/mkdir -p "$DEBS_DEST" + cp -R "$deb" "$DEBS_DEST/" + deb_count=$((deb_count + 1)) +done +if (( deb_count > 0 )); then + echo " [+] Staged $deb_count extra deb(s) for first-boot install" +else + echo " [=] No extra debs to stage" +fi + JB_DIR_NAME="jb-vphone" /bin/rm -rf $MNT5/$BOOT_HASH/jb /bin/rm -rf $MNT5/$BOOT_HASH/$JB_DIR_NAME diff --git a/scripts/cfw_install_jb.sh b/scripts/cfw_install_jb.sh index 1381873..2059f13 100755 --- a/scripts/cfw_install_jb.sh +++ b/scripts/cfw_install_jb.sh @@ -270,6 +270,24 @@ if [[ -f "$SILEO_DEB" ]]; then cp -R "$SILEO_DEB" "$MNT5/$BOOT_HASH/org.coolstar.sileo_2.5.1_iphoneos-arm64.deb" fi +# ── Extra debs: download from manifest, then stage the whole cache ────── +echo " Fetching extra debs..." +zsh "$SCRIPT_DIR/fetch_debs.sh" || true +DEBS_CACHE="${SCRIPT_DIR:h}/debs" +DEBS_DEST="$MNT5/$BOOT_HASH/debs" +/bin/rm -rf "$DEBS_DEST" +deb_count=0 +for deb in "$DEBS_CACHE"/*.deb(N); do + (( deb_count == 0 )) && /bin/mkdir -p "$DEBS_DEST" + cp -R "$deb" "$DEBS_DEST/" + deb_count=$((deb_count + 1)) +done +if (( deb_count > 0 )); then + echo " [+] Staged $deb_count extra deb(s) for first-boot install" +else + echo " [=] No extra debs to stage" +fi + JB_DIR_NAME="jb-vphone" /bin/rm -rf $MNT5/$BOOT_HASH/jb /bin/rm -rf $MNT5/$BOOT_HASH/$JB_DIR_NAME diff --git a/scripts/fetch_debs.sh b/scripts/fetch_debs.sh new file mode 100755 index 0000000..8dc78c3 --- /dev/null +++ b/scripts/fetch_debs.sh @@ -0,0 +1,82 @@ +#!/bin/zsh +# fetch_debs.sh — Download debs from a URL manifest into a cache dir. +# Skips files already cached; leaves manually-added debs alone. A failed +# download is reported and skipped, never fatal (always exits 0). +# +# Usage: fetch_debs.sh [cache_dir] [manifest_file] +# defaults: /debs and /debs.list + +set -uo pipefail + +[[ -n "${_VPHONE_PATH:-}" ]] && export PATH="$_VPHONE_PATH" + +SCRIPT_DIR="${0:a:h}" +REPO_ROOT="${SCRIPT_DIR:h}" + +CACHE_DIR="${1:-$REPO_ROOT/debs}" +MANIFEST="${2:-$REPO_ROOT/debs.list}" + +mkdir -p "$CACHE_DIR" + +deb_filename_from_url() { + local url="$1" + url="${url%%\#*}" + url="${url%%\?*}" + print -r -- "${url##*/}" +} + +downloaded=0 +cached=0 +failed=0 + +if [[ -f "$MANIFEST" ]]; then + while IFS= read -r line || [[ -n "$line" ]]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [[ -z "$line" || "$line" == \#* ]] && continue + + name="$(deb_filename_from_url "$line")" + if [[ "$name" != *.deb ]]; then + echo " [!] ERROR: URL does not resolve to a .deb filename, skipping: $line" >&2 + failed=$((failed + 1)) + continue + fi + + dest="$CACHE_DIR/$name" + if [[ -s "$dest" ]]; then + echo " [=] Cached: $name" + cached=$((cached + 1)) + continue + fi + + echo " [>] Downloading: $line" + tmp="$dest.download" + if curl -fL --retry 2 --connect-timeout 20 \ + --speed-limit 1024 --speed-time 30 -o "$tmp" "$line"; then + mv -f "$tmp" "$dest" + echo " [+] Downloaded: $name" + downloaded=$((downloaded + 1)) + else + rc=$? + rm -f "$tmp" + echo " [!] ERROR: download failed (curl exit $rc), skipping: $line" >&2 + failed=$((failed + 1)) + fi + done < "$MANIFEST" +else + echo " [=] No manifest at $MANIFEST (skipping downloads)" +fi + +total=0 +for f in "$CACHE_DIR"/*.deb(N); do + total=$((total + 1)) +done + +echo " [i] debs: $total in cache ($downloaded downloaded, $cached already cached, $failed failed)" + +# Return the cache to the invoking user when run under sudo. +if [[ "$(id -u)" == "0" && -n "${SUDO_USER:-}" ]]; then + chown -R "$SUDO_USER" "$CACHE_DIR" 2>/dev/null || true +fi + +exit 0 diff --git a/scripts/vphone_jb_setup.sh b/scripts/vphone_jb_setup.sh index ff8ca02..696a598 100755 --- a/scripts/vphone_jb_setup.sh +++ b/scripts/vphone_jb_setup.sh @@ -171,6 +171,44 @@ else fi fi +# ═══════════ 5b/8 INSTALL EXTRA DEBS ═════════════════════════ +log "[5b/8] Installing extra debs..." +DEBS_DIR="/private/preboot/$BOOT_HASH/debs" +if [ -d "$DEBS_DIR" ]; then + to_install=() + for deb in "$DEBS_DIR"/*.deb; do + [ -f "$deb" ] || continue + name="$(basename "$deb")" + pkg="$(dpkg-deb -f "$deb" Package 2>/dev/null)" + ver="$(dpkg-deb -f "$deb" Version 2>/dev/null)" + if [ -z "$pkg" ]; then + log " WARNING: cannot read Package field from $name, will install anyway" + to_install+=("$deb") + continue + fi + cur="$(dpkg-query -W -f='${Version}' "$pkg" 2>/dev/null)" + if [ -n "$cur" ] && dpkg --compare-versions "$cur" ge "$ver" 2>/dev/null; then + log " $pkg $cur already installed (>= $ver), skipping" + else + to_install+=("$deb") + fi + done + if [ "${#to_install[@]}" -gt 0 ]; then + names="$(for d in "${to_install[@]}"; do basename "$d"; done | tr '\n' ' ')" + log " Installing ${#to_install[@]} deb(s): $names" + if dpkg -i "${to_install[@]}"; then + log " Extra debs installed" + else + rc=$? + log " WARNING: dpkg -i exited with $rc (unmet external deps are non-fatal)" + fi + else + log " All extra debs already installed, nothing to do" + fi +else + log " No extra debs staged" +fi + uicache -a 2>/dev/null || true log " uicache refreshed"