proot multistrap study - hanyong/note GitHub Wiki

proot 和 multistrap 学习笔记

debootstrap 可以搭建一套 mini 的 debian (ubuntu) 系统, 但有几个问题:

  1. 需要 root 权限
  2. debootstrap 搭建了一套完整的系统, 包括设备, 文件系统挂载, 网络, 用户等, 这部分希望重用外部系统的,特别一些操作需要 root 权限.

解决办法1:

fakechroot, fakeroot 配合 debootstrap 使用, 能够安装部分软件包, 一些确实需要 root 权限的包安装失败, 并导致 apt 也安装失败, 最终系统几乎不可用.

解决办法2:

proot, proot 实现了 fakeroot, chroot 和文件系统绑定的功能, 而且不需要 root 权限,刚好是我们想要的.

剩下的问题是, debootstrap 构建了一个完整系统, 而我们只需要一个应用层 (包管理系统), 所以剩下的问题就是, 有没有办法把 debootstrap 自定义成一个软件包管理系统, 拿掉其他操作系统组件. 与 brew 不同, brew 重用了操作系统应用层的部分软件包, 进行扩展并与操作系统软件包共存. 而我们希望在完全重建应用层, 完全不依赖操作系统的应用层.

我们把操作系统拆为几个层面:

  1. 系统内核
  2. 设备、网络管理, 特殊文件系统 (/dev/, /dev/pts/, /proc/, /sys/)
  3. 应用层, 软件包管理器
  4. root 权限

3 的部分软件包用于管理 2. 我们希望完全重建 (自定义和可控) 3, 但重用操作系统提供的 1 和 2, 我们重建的 3 不包括管理 2 的部分, 要操作或管理 2, 要么 (1) 将 os 的部分软件包引入 guest (可能应用基础软件包重建无法运行?) (2) 在外围操作系统操作, guest 只在普通用户态使用操作系统 1, 2 提供的能力.

我们使用 1, 2, 完全控制 3. 少数操作依赖 4, (1) 应用通过 sid 暴露能力 (注意安全风险) (2) 少数非日常操作, 找管理员完成.

研究了一下 debootstrap, 其每个 SUITE 安装时会调用 /usr/share/debootstrap/scripts/ 下的同名脚本, 如 /usr/share/debootstrap/scripts/trusty, 因为个发行版的安装逻辑都一样, 只是在软件仓库不同子目录下, 这些脚本多是软链接到同一个文件.

observer.hany@ali-59375n:/usr/share/debootstrap/scripts$ ll trusty 
lrwxrwxrwx 1 root root 5 11月  3 23:35 trusty -> gutsy

这个脚本是一个 sh 脚本, 如果要修改自定义, 可以拷贝一份出来修改.

其中有个函数 work_out_debs 是计算需要安装的软件包, 其逻辑是从软件仓库中找出 Priority: required 的包作为 required, 再根据安装 variant 确定 base 软件包, minbase 最少的 base 软件包只有 apt 一个, 如果要 apt 支持 https 协议, 则再加上 apt-transport-https ca-certificates 两个.

work_out_debs () {
	required="$(get_debs Priority: required)"

	if doing_variant -; then
		#required="$required $(get_debs Priority: important)"
		#  ^^ should be getting debconf here somehow maybe
		base="$(get_debs Priority: important)"
	elif doing_variant buildd; then
		base="$(get_debs Build-Essential: yes)"
	elif doing_variant fakechroot || doing_variant minbase; then
		base="apt"
	fi

	case $MIRRORS in
	    https://*)
		base="$base apt-transport-https ca-certificates"
		;;
	esac
}

以安装 trusty 为例, debootstrap 从仓库上获取软件包信息文件, 保存到 ${TARGET}/var/lib/apt/lists/debootstrap.invalid_dists_trusty_main_binary-amd64_Packages, 其中 Priority: required 有 97 个.

cdebootstrap 是保存到 ${TARGET}/var/cache/bootstrap/_dists_._main_binary-amd64_Packages, cdebootstrap 查找软件包的逻辑没找到, 好像比 Priority: required 要少一些, trusty 各类 Priority: 软件包统计如下:

$ grep '^Priority: ' ubu-min/var/cache/bootstrap/_dists_._main_binary-amd64_Packages | python2 -c '              
import sys
d = {}
for line in sys.stdin:
  try:
    d[line] += 1
  except:
    d[line] = 1
print d
'
{'Priority: extra\n': 1728, 'Priority: standard\n': 141, 'Priority: optional\n': 6513, 'Priority: required\n': 97, 'Priority: import
ant\n': 87}

看了下 linux-image-* 软件包属于 Priority: optional. 对 guest 应用层来说, kernel 由 host 提供, 并不需要.

login, passwd 等默认也属于 Priority: required, 要做一个最小的应用层核心, 可以只保留 bashapt 即可, 其他全部留空. 为了做到这点, 简单的办法就是直接修改 work_out_debs 函数, 添加一行把 required 直接替换为 bash (或自定义添加其他软件包), base 保持为 apt. debootstrap 会自动解析安装依赖的软件包.

修改后可运行 debootstrap --print-debs --keep-debootstrap-dir 测试查看安装软件包列表. 使用如下命令可以使用不同参数设置不段重试安装同时避免重复下载 deb 包:

find ubu-min -type f | grep -vF -e '.deb' -e '/var/lib/apt/lists/debootstrap.invalid' | xargs rm -vf && fakechroot fakeroot-ng debootstrap --keep-debootstrap-dir --variant=fakechroot trusty ubu-min/ http://mirrors.aliyuncs.com/ubuntu/

required 设置为 apt, 与 base 会自动合并去重, 即指定只安装 apt 一个软件包, 安装报错, 从日志看到如下报错日志:

dpkg: warning: 'sh' not found in PATH or not executable
dpkg: warning: 'rm' not found in PATH or not executable
dpkg: warning: 'find' not found in PATH or not executable
dpkg: warning: 'ldconfig' not found in PATH or not executable
dpkg: error: 4 expected programs not found in PATH or not executable

required 设置为 libc-bin bash coreutils findutils, 再次安装报错:

W: Failure trying to run: chroot /home/hanyong/rootfs/ubu-min dpkg --force-depends --install
W: See /home/hanyong/rootfs/ubu-min/debootstrap/debootstrap.log for details

从日志看:

dpkg: error: --install needs at least one package archive file argument

从错误看应该是调用 dpkg --install 时缺少软件包参数, 可能是我们省略了太多软件包, 导致某些执行路径上安装软件包参数为空. 从之前安装脚本找到, 这个命令应该是在二阶段安装时安装核心组件调用的.

#second_stage_install () {
	x_core_install () {
		smallyes '' | in_target dpkg --force-depends --install $(debfor "$@")
	}
	
	# ... ... 随后有几个地方调用了 x_core_install ... ...
	
	p; progress $baseprog $bases INSTCORE "Installing core packages" #2
	ln -sf mawk "$TARGET/usr/bin/awk"
	x_core_install base-files base-passwd
	p; progress $baseprog $bases INSTCORE "Installing core packages" #3
	x_core_install dpkg

	if [ ! -e "$TARGET/etc/localtime" ]; then
		ln -sf /usr/share/zoneinfo/Etc/UTC "$TARGET/etc/localtime"
	fi

	if doing_variant fakechroot; then
		install_fakechroot_tools
	fi

	p; progress $baseprog $bases INSTCORE "Installing core packages" #4
	x_core_install $LIBC

	p; progress $baseprog $bases INSTCORE "Installing core packages" #5
	x_core_install perl-base

	p; progress $baseprog $bases INSTCORE "Installing core packages" #6
	rm "$TARGET/usr/bin/awk"
	x_core_install mawk

	p; progress $baseprog $bases INSTCORE "Installing core packages" #7
	if doing_variant -; then
		x_core_install debconf
	fi

x_core_install() 添加调试日志 echo x_core_install: ${@} => $(debfor "${@}")", 也可以直接在脚本开头添加 set -x 查看所有命令执行调试信息. echo 添加的日志重定向到了 debootstrap.log 文件中, 注意这段日志出现在 "dpkg: warning" 之前, 一开始还没看到:

x_core_install: base-passwd => 
dpkg: warning: parsing file '/var/lib/dpkg/status' near line 4 package 'dpkg':

从日志看安装 base-passwd 时就出现安装的包为空了. 用 --print-debs 看了下我们当前会安装的包, 修改 required 再加上 base-passwd perl-base mawk debconf 几个包. 安装时还是报错, 看日志安装脚本依赖 sed, 再加上 sed 包. 这次没有报错, 但看日志还有报 grep 没有找到, grep 也加上. 另外最后报了一句 sed: can't read /home/hanyong/rootfs/ubu-min/var/lib/dpkg/info/initscripts.postinst: No such file or directory. 看 proot -R 默认不重用 /etc/init.d/, 可以安装不冲突, 但 initscripts 我们用不到. 测试增加 initscripts 后多了 sysv-rc 等很多系统相关的软件包, 作为应用层, 这些东西没什么用, 暂时不要.

安装后得到一个最小系统包含 40 个软件包如下 (可使用 --print-debs 提前看到这些包):

base-files
base-passwd
bash
coreutils
dash
debconf
debianutils
dpkg
findutils
gcc-4.9-base
grep
libacl1
libattr1
libbz2-1.0
libc-bin
libc6
libcap2
libdebconfclient0
libgcc1
liblzma5
libpcre3
libselinux1
libtinfo5
mawk
multiarch-support
perl-base
sed
sensible-utils
tar
zlib1g
apt
gcc-4.8-base
gnupg
gpgv
libapt-pkg4.12
libreadline6
libstdc++6
libusb-0.1-4
readline-common
ubuntu-keyring

可使用 fakechroot fakeroot-ng chrootproot -R 进入安装好的根系统. 使用 proot 需要先删掉 dev, proc 两个影响 proot 的软链接. 前者貌似 shell 功能要完善一些, 对普通用户应用层来说, 后者可能更好. 但两种情况下都发现 apt-get 没能正常安装使用, 手动调用 dpkg --install /var/cache/apt/archives/apt_1.0.1ubuntu2_amd64.deb 报如下错误:

dpkg: dependency problems prevent configuration of apt:
 apt depends on libapt-pkg4.12 (>= 0.9.16); however:
  Package libapt-pkg4.12 is not installed.
 apt depends on libc6 (>= 2.15); however:
  Package libc6:amd64 is not configured yet.
 apt depends on libgcc1 (>= 1:4.1.1); however:
  Package libgcc1:amd64 is not configured yet.
 apt depends on libstdc++6 (>= 4.6); however:
  Package libstdc++6 is not installed.
 apt depends on ubuntu-keyring; however:
  Package ubuntu-keyring is not installed.
 apt depends on gnupg; however:
  Package gnupg is not installed.

试了一下 libc6, libgcc1, multiarch-support 循环依赖并且都没办法正确安装好.

之后尝试了一下 multistrap, ubuntu 14.04 上 multistrap 有个 bug, /usr/sbin/multistrap 脚本文件第 989 行有个多余的 $forceyes 变量, 打开脚本文件到 989 行直接删掉这个变量就 ok 了. 运行 multistrap 需要写一个配置文件, 稍微有一点学习成本, 但是稍微看了一下 man 手册页和 /usr/share/multistrap/ 下的示例, 其实也很简单. 这个配置文件类似于 debootstrap 对应的脚本, 控制一些安装选项, 但操作修改更简单, 更灵活. 其配置项分为 [General] 段和其他 section 段, 全局配置都放在 [General] 段, 一个 section 段配置一个仓库源及对应安装的软件包. 配置项都使用全小写拼写. 其有一个 omitrequired=true 可以直接控制省略仓库源中的 required 软件包, 只安装指定的软件包, 好像刚好符合我们的需求. 运行 multistrap 时还可以添加 --dry-run 先预览一下相关安装配置项.

与 debootstrap 不同, debootstrap 是自身实现软件包下载安装功能, multistrap 是调用 host 系统提供的 apt 软件包进行下载安装, 但安装到指定的 ${TARGET} 目录. 所以 multistrap 更强大, 并且天生不需要 root 权限 (指定 apt 安装到用户目录时不需要 root 权限). apt 不能直接在用户目录完成完整安装, 所以 multistrap 的安装过程大概是先调用 apt 完成软件包下载和解压, 此时完全不需要 root 权限; 然后 chroot 到安装系统根目录执行 preinst 脚本和 dpkg --configure -a 完成安装, 这一步需要 root 权限, 显然我们以非 root 执行 multistrap 时这一步会失败。 multistrap 还可以完成与主机不同 arch 架构的 bootstrap 安装, 此时不会执行 preinst 和 configure. 我们可以添加 ignorenativearch=true 在与主机相同的 arch 下也不执行 configure, 即与安装其他 arch 相同. 即相当于预安装, 在有 root 权限时再 chroot 到安装根据目录手动完成配置. ubuntu 14.04 下貌似有 bug, --dry-run 时看到 ignorenativearch=true 没有生效, 但实际是生效的. 这个也很适合我们, 由于我们没有 root 权限, 可以先完成预安装, 再通过 proot -0 -b /dev -b /proc -b /sys -r ${TARGET}fakechroot fakeroot-ng chroot ${TARGET} 到安装目录下完成 configure. 使用 proot 更好, 感觉支持更好, 并且可以实现自动帮我们挂载 "/dev", "/proc", "/sys" 等重要目录.

参考 "man multistrap" 中 "Environment" 一段的说明, 由于 locales 等没有配置, 执行 configure 时我们要设置下 LANG=C. man 说明如下:

Environment
       To configure the unpacked packages (whether in native or cross mode), certain environment variables are needed:

       Debconf needs to be told to accept that user interaction is not desired:

        DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true

       Perl needs to be told to accept that no locales are available inside the chroot and not to complain:

        LC_ALL=C LANGUAGE=C LANG=C

       Then, dpkg can configure the packages:

       chroot method (PATH = top directory of chroot):

        DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \
        LC_ALL=C LANGUAGE=C LANG=C chroot /PATH/ dpkg --configure -a

       at a login shell:

        # export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true
        # export LC_ALL=C LANGUAGE=C LANG=C
        # dpkg --configure -a

       (As above, dpkg needs /proc and /sysfs mounted first.)

debconf 相关的配置先不管, locale 相关的环境变量我们只设置 LANG, 其他全部 unset, 这样修改时也只用修改 LANG. 使用 proot 进入 guest 系统后, 所有环境变量都跟原用户一样, 初次进入系统进行 configure 时可先预设好基本的环境变量, 安装好系统后或许可以通过相关脚本重置环境变量。 一些基本文件, 如 /etc/host.conf, /etc/passwd, guest 可以复用 host 的, 但为了能够访问网络, 可以拷贝 host /etc/resolv.conf 文件到 guest. 但初次 configure 时可能要处理这些文件, 先不要跟 host 复用. 初次进入 guest 系统可使用如下命令:

#cp /etc/resolv.conf ubu1404/etc/
LANG=C PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ubu1404/sbin/proot-x86_64 -0 -b /dev -b /proc -b /sys -r ubu1404

然后执行命令:

dpkg --configure -a

完成初始 configure, 或执行 apt-get 安装更多软件包.

可完成配置的最少 packages, multistrap 配置如下:

[General]
bootstrap=Ubuntu
aptsources=Ubuntu
omitrequired=true
#omitpreinst=false
ignorenativearch=true

[Ubuntu]
packages=libc-bin coreutils findutils mawk sed grep dash apt
#bash passwd hostname
source=http://mirrors.ustc.edu.cn/ubuntu/
keyring=ubuntu-keyring
suite=trusty
#components=main restricted universe multiverse
components=main
omitdebsrc=true

包含一些最基本软件包和 apt。 其实使用 dpkg 就可以完成初始 configure, 使用 apt 代替 dpkg 有两个好处: (1) configure 需要一些初始的空文件夹, apt 解压时会自动创建 /var/log 文件夹, dpkg 不会, 需要手动创建. (2) 可以直接利用 apt-get 安装其他软件包.

multistrap 也支持一个 tarballname 配置, 不过这个配置默认会删除 build 文件夹, 也没有自己打包灵活, 所以我们不使用.

rm -rfv * && multistrap -d . -f ../ubu1404.conf
rsync -av ~/software/proot-x86_64 sbin/
LANG=C sbin/proot-x86_64 -0 -b /dev -b /proc -b /sys -r . dpkg --configure -a

初始 configure, 仅手动绑定少量 host 路径, 一开始许多基本系统文件还没有, 安装配置 base-passwd, base-files 等基本软件包时避免 /etc/passwd, /etc/host.conf 等文件与 host 文件冲突。 设置 `LANG=C'。 因为 proot 会直接重用 host 的环境变量, 在非 ubuntu 系统上执行初始 configure 最好同时设置好 PATH:

#LANG=C PATH=/usr/sbin:/usr/bin:/sbin:/bin sbin/proot-x86_64 -0 -b /dev -b /proc -b /sys -r . dpkg --configure -a
LANG=C sbin/proot-x86_64 -0 -b /dev -b /proc -b /sys -r . dpkg --configure -a

使用 apt 安装系统基本软件包:

LANG=C -0 -b /dev -b /proc -b /sys -b /etc/resolv.conf -r ubu1404 apt-get --no-install-recommends install base-passwd base-files bash locales

locales 也是很基本的软件包, 但 locales 的 postinst 脚本使用了 bash, 需要先安装 bash. 所以先同时安装上 base-passwd base-files bash locales 这 4 个软件包, 推荐依赖先不安装.

之后可以使用 proot -S 进入 guest 安装更多其他软件包, 或做其他配置. 执行 locale-gen en_US.UTF-8 失败:

$ LANG=C ~/software/proot-x86_64 -S ubu1404 locale-gen en_US.UTF-8
Generating locales...
  en_US.UTF-8... character map file `UTF-8' not found: No such file or directory
default character map file `ANSI_X3.4-1968' not found: No such file or directory
failed
Generation complete.
$ find /usr/share/i18n/ -name '*UTF-8*'
/usr/share/i18n/charmaps/UTF-8.gz

但看了下 UTF-8 这个文件明明是存在的, 不知道为什么, 可能是缺少什么软件包, 不知道少什么. 跟踪了一下执行到 localedef 时报错:

+ localedef -i en_US -c -f UTF-8 en_US.UTF-8
character map file `UTF-8' not found: No such file or directory

看了下 "man localedef", "-f" 从默认路径找文件, 应该没问题. 由于文件是 ".gz" 格式, 感觉是缺少 gzip 包导致.

干脆先把 gzip bzip2 xz-utils p7zip-full 一次全装上. p7zip-full 在 universe 组件, 先配置下 "sources.list", 更新软件包.

sed -i 's/^deb/#deb/' etc/apt/sources.list.d/multistrap-ubuntu.list
echo > etc/apt/sources.list 'deb http://mirrors.ustc.edu.cn/ubuntu/ trusty main restricted universe multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ trusty-updates main restricted universe multiverse'
LANG=C ~/software/proot-x86_64 -S . apt-get update

再执行安装:

LANG=C ~/software/proot-x86_64 -S . apt-get install gzip bzip2 xz-utils p7zip-full

再执行 locale-gen 果然成功了:

LANG=C ~/software/proot-x86_64 -S . locale-gen en_US.UTF-8 zh_CN.UTF-8 zh_CN.GBK zh_CN.GB18030

构建好 locales 后, 后续进入 guest 就不必设置 LANG=C 了.

总结: 许多软件包的安装配置都依赖一些基础软件包, 但是其依赖管理上没说明这些依赖, 可能因为它们假设这些依赖包必定是已经存在的。 所以, 最小安装默认把 Priority: required 全部安装上也是有道理的. 只是一些软件包的安装配置试图修改 /dev, 启动服务等强依赖 root 权限, 安装配置会有问题, 得想办法绕过去. 为了简化, 我们干脆不安装 required, 结果也踩了一些坑.

回到 debootstrap, debootstrap 有一个 fakechroot variant 提供了一些绕过 root 权限的 work around, 看能否再利用一下. 由于之前的尝试 debootstrap fakechroot 默认安装是有问题的, 我们调整一下: (1) 梳理最小软件包 (2) 添加 --foreign 做两阶段安装方便在第2阶段做各种尝试.

foreign 第一阶段安装后相关脚本文件都拷贝到了 ${TARGET}/debootstrap/ 目录下, 可以在这里分析而阶段安装的过程, 主要的 3 个脚本文件是 debootstrap, functions, suite-script. fakechroot 默认会把 /proc, /dev 建成软链接, 这是不合理, 我们用 proot 绑定目录, 希望 fakechroot 不要做任何处理. functions 脚本找到 setup_proc_fakechroot(), setup_devices_fakechroot() 两个函数,将对应函数体置空.

第二阶段脚本运行时也会检查 fakechroot 环境, 看了下脚本其只是检查 FAKECHROOT 变量, 而相关脚本中没有设置该变量的地方, 猜想是 fakechroot 设置的, 运行脚本前直接设置环境变量 FAKECHROOT=true, 发现可以运行通过. 但最后有几个警告:

I: Configuring apt...
I: Unpacking the base system...
W: Failure while installing base packages.  This will be re-attempted up to five times.
W: See //debootstrap/debootstrap.log for details (possibly the package archive is at fault)
W: Failure while installing base packages.  This will be re-attempted up to five times.
W: See //debootstrap/debootstrap.log for details (possibly the package archive is at fault)
W: Failure while installing base packages.  This will be re-attempted up to five times.
W: See //debootstrap/debootstrap.log for details (possibly the package archive is at fault)
W: Failure while installing base packages.  This will be re-attempted up to five times.
W: See //debootstrap/debootstrap.log for details (possibly the package archive is at fault)
W: Failure while installing base packages.  This will be re-attempted up to five times.
W: See //debootstrap/debootstrap.log for details (possibly the package archive is at fault)
I: Configuring the base system...
I: Base system installed successfully.

debootstrap.log 报:

dpkg: error: --unpack needs at least one package archive file argument

中间也有几个 unpack 警告, 应该还是缺少软件包. 看了下 suite-script 脚本, 安装软件包是分为 required 和 base 的, 最小安装 base 默认只有一个 apt 包, 可能是因为 required 也包含了这个包导致去重后 base 为空, 从 required 中去掉 apt 再试. 最后还报一个错:

Errors were encountered while processing:
 /var/cache/apt/archives/dash_0.5.7-4ubuntu1_amd64.deb
sed: can't read //var/lib/dpkg/info/initscripts.postinst: No such file or directory

应该是 initscripts 未安装, 既然我们启用了 fakechroot, 把 initscripts 软件包也加上试试. 结果引入了太多包, 还是安装失败. grep 了一把 TARGET 目录, 发现还是 suite-script 脚本引用了 initscripts.postinst:

	if doing_variant fakechroot; then
	# fix initscripts postinst (no mounting possible, and wrong if condition)
	sed -i '/dpkg.*--compare-versions/ s/\<lt\>/lt-nl/' "$TARGET/var/lib/dpkg/info/initscripts.postinst"
	fi

修改下脚本 "$TARGET/var/lib/dpkg/info/initscripts.postinst" 不存在时不要执行这个 sed.

sed -i '/# fix initscripts postinst/ a \'$'\n\t''test -w "$TARGET/var/lib/dpkg/info/initscripts.postinst" && ' debootstrap/suite-script

重试发现还是会报一个错误, dash 没有配置成功, 原因未知:

Selecting previously unselected package dash.
Preparing to unpack .../dash_0.5.7-4ubuntu1_amd64.deb ...
dpkg (subprocess): unable to execute new pre-installation script (/var/lib/dpkg/tmp.ci/preinst): No such file or directory
dpkg: error processing archive /var/cache/apt/archives/dash_0.5.7-4ubuntu1_amd64.deb (--unpack):
 subprocess new pre-installation script returned error exit status 2

比较了一下 fakechroot 和 minbase, first-stage 安装结果基本是一样的, 除了 ${TARGET}/debootstrap/variant 被设置为 fakechroot.

再看 second-stage, 主要是 setup_proc(), setup_devices() 处理不一样, 然后额外执行了 install_fakechroot_tools().

	if doing_variant fakechroot; then
		install_fakechroot_tools
	fi

install_fakechroot_tools() 主要是改写了 /sbin/ldconfig/usr/bin/ldd, 貌似也没什么必要. 这么说我们只要把 setup_proc()setup_devices() 全部置空, variant 不用 fakechroot 也能安装.

sed -r '/^setup_(proc|devices)(_fakechroot)? / a \'$'\n\treturn 0\n' debootstrap/functions

另外看了下安装脚本, configure 软件包前会先 fake initctl 等文件, 安装完再恢复. 我们只作为应用层, 其实可以一直 fake 不用恢复.

	echo \
"#!/bin/sh
exit 101" > "$TARGET/usr/sbin/policy-rc.d"
	chmod 755 "$TARGET/usr/sbin/policy-rc.d"

	mv "$TARGET/sbin/start-stop-daemon" "$TARGET/sbin/start-stop-daemon.REAL"
	echo \
"#!/bin/sh
echo
echo \"Warning: Fake start-stop-daemon called, doing nothing\"" > "$TARGET/sbin/start-stop-daemon"
	chmod 755 "$TARGET/sbin/start-stop-daemon"

	if [ -x "$TARGET/sbin/initctl" ]; then
	  mv "$TARGET/sbin/initctl" "$TARGET/sbin/initctl.REAL"
	  echo \
"#!/bin/sh
if [ \"\$1\" = version ]; then exec /sbin/initctl.REAL \"\$@\"; fi
echo
echo \"Warning: Fake initctl called, doing nothing\"" > "$TARGET/sbin/initctl"
	  chmod 755 "$TARGET/sbin/initctl"
	fi

... ...
	
	if [ -x "$TARGET/sbin/initctl.REAL" ]; then
		mv "$TARGET/sbin/initctl.REAL" "$TARGET/sbin/initctl"
	fi
	mv "$TARGET/sbin/start-stop-daemon.REAL" "$TARGET/sbin/start-stop-daemon"
	rm -f "$TARGET/usr/sbin/policy-rc.d"

我们将上面这两段代码补充完善一下, 抽出两个独立的脚本 fakeinit.sh, unfakeinit.sh:

cat > sbin/fakeinit.sh <<'EOF'
#!/bin/sh

if ! [ -e "$TARGET/usr/sbin/policy-rc.d.REAL" ]; then
	test -e "$TARGET/usr/sbin/policy-rc.d" || touch "$TARGET/usr/sbin/policy-rc.d"
	mv -T "$TARGET/usr/sbin/policy-rc.d" "$TARGET/usr/sbin/policy-rc.d.REAL"
	echo \
"#!/bin/sh
exit 101" > "$TARGET/usr/sbin/policy-rc.d"
	chmod 755 "$TARGET/usr/sbin/policy-rc.d"
fi

if ! [ -e "$TARGET/sbin/start-stop-daemon.REAL" ]; then
	mv "$TARGET/sbin/start-stop-daemon" "$TARGET/sbin/start-stop-daemon.REAL"
	echo \
"#!/bin/sh
echo
echo \"Warning: Fake start-stop-daemon called, doing nothing\"" > "$TARGET/sbin/start-stop-daemon"
	chmod 755 "$TARGET/sbin/start-stop-daemon"
fi

if ! [ -e "$TARGET/sbin/initctl.REAL" ]; then
	if [ -x "$TARGET/sbin/initctl" ]; then
	  mv "$TARGET/sbin/initctl" "$TARGET/sbin/initctl.REAL"
	  echo \
"#!/bin/sh
if [ \"\$1\" = version ]; then exec /sbin/initctl.REAL \"\$@\"; fi
echo
echo \"Warning: Fake initctl called, doing nothing\"" > "$TARGET/sbin/initctl"
	  chmod 755 "$TARGET/sbin/initctl"
	fi
fi
EOF
chmod 755 sbin/fakeinit.sh

cat > sbin/unfakeinit.sh <<'EOF'
#!/bin/sh

	if [ -x "$TARGET/sbin/initctl.REAL" ]; then
		mv "$TARGET/sbin/initctl.REAL" "$TARGET/sbin/initctl"
	fi

if [ -x "$TARGET/sbin/start-stop-daemon.REAL" ]; then
	mv "$TARGET/sbin/start-stop-daemon.REAL" "$TARGET/sbin/start-stop-daemon"
fi

if [ -e "$TARGET/usr/sbin/policy-rc.d.REAL" ]; then
	mv -T "$TARGET/usr/sbin/policy-rc.d.REAL" "$TARGET/usr/sbin/policy-rc.d"
	test -z "$(cat "$TARGET/usr/sbin/policy-rc.d")" && rm -f "$TARGET/usr/sbin/policy-rc.d"
fi
EOF
chmod 755 sbin/unfakeinit.sh
⚠️ **GitHub.com Fallback** ⚠️