add debug initramfs as seperate package

"proxmox-backup-restore-image-debug", containing only the debug
initramfs, so depends on the base "proxmox-backup-restore-image" for the
kernel.

Adapt the init-shim to start an agetty on ttyS1, which the host
can use to connect to a root shell for debugging, and use
create_dir_all, since some debug packages seem to create /sys and /proc
as empty dirs already.

The build_initramfs.sh script is modified to include dependency
resolution via apt-rdepends, so debug packages like agetty (util-linux),
busybox and gdb can easily be added. This now builds both the regular
and the debug binary at once, to avoid downloading shared packages
twice.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
This commit is contained in:
Stefan Reiter 2021-05-06 17:26:24 +02:00 committed by Thomas Lamprecht
parent 37456b06e8
commit 2a1ef81fca
8 changed files with 106 additions and 27 deletions

View File

@ -2,11 +2,14 @@ include /usr/share/dpkg/pkg-info.mk
include /usr/share/dpkg/architecture.mk include /usr/share/dpkg/architecture.mk
PACKAGE=proxmox-backup-restore-image PACKAGE=proxmox-backup-restore-image
PACKAGE_DBG=proxmox-backup-restore-image-debug
BUILDDIR=${PACKAGE}-${DEB_VERSION_UPSTREAM_REVISION} BUILDDIR=${PACKAGE}-${DEB_VERSION_UPSTREAM_REVISION}
DEB=${PACKAGE}_${DEB_VERSION}_${DEB_BUILD_ARCH}.deb DEB=${PACKAGE}_${DEB_VERSION}_${DEB_BUILD_ARCH}.deb
DSC=${PACKAGE}_${DEB_VERSION}.dsc DSC=${PACKAGE}_${DEB_VERSION}.dsc
DEB_DBG=${PACKAGE_DBG}_${DEB_VERSION}_${DEB_BUILD_ARCH}.deb
DSC_DBG=${PACKAGE_DBG}_${DEB_VERSION}.dsc
all: deb all: deb
@ -32,21 +35,23 @@ ${BUILDDIR}: submodules.prepared
deb: ${DEB} deb: ${DEB}
${DEB}: ${BUILDDIR} ${DEB}: ${BUILDDIR}
cd ${BUILDDIR}; dpkg-buildpackage -b -us -uc cd ${BUILDDIR}; dpkg-buildpackage -b -us -uc
lintian ${DEB} lintian ${DEB} ${DEB_DBG}
${DEB_DBG}: ${DEB}
.PHONY: dsc .PHONY: dsc
dsc: ${DSC} dsc: ${DSC}
${DSC}: ${BUILDDIR} ${DSC}: ${BUILDDIR}
cd ${BUILDDIR}; dpkg-buildpackage -S -us -uc -d cd ${BUILDDIR}; dpkg-buildpackage -S -us -uc -d
lintian ${DSC} lintian ${DSC} ${DSC_DBG}
${DSC_DBG}: ${DSC}
.PHONY: dinstall .PHONY: dinstall
dinstall: deb dinstall: deb
dpkg -i ${DEB} dpkg -i ${DEB} ${DEB_DBG}
.PHONY: upload .PHONY: upload
upload: ${DEB} upload: ${DEB}
tar cf - ${DEB} | ssh -X repoman@repo.proxmox.com upload --product pbs,pve --dist buster tar cf - ${DEB} ${DEB_DBG} | ssh -X repoman@repo.proxmox.com upload --product pbs,pve --dist buster
.PHONY: clean .PHONY: clean
clean: clean:

10
debian/control vendored
View File

@ -2,7 +2,8 @@ Source: proxmox-backup-restore-image
Section: admin Section: admin
Priority: optional Priority: optional
Maintainer: Proxmox Support Team <support@proxmox.com> Maintainer: Proxmox Support Team <support@proxmox.com>
Build-Depends: asciidoc-base, Build-Depends: apt-rdepends,
asciidoc-base,
automake, automake,
bc, bc,
bison, bison,
@ -34,3 +35,10 @@ Description: Kernel/initramfs images for Proxmox Backup single-file restore.
Preconfigured images used as base for single file restore of Proxmox Backup Preconfigured images used as base for single file restore of Proxmox Backup
Server snapshots. Not really useful on their own, so best used together with Server snapshots. Not really useful on their own, so best used together with
the proxmox-backup-file-restore package, which provide the actual tools. the proxmox-backup-file-restore package, which provide the actual tools.
Package: proxmox-backup-restore-image-debug
Architecture: amd64
Depends: proxmox-backup-restore-image
Description: Debug initramfs image for Proxmox Backup single-file restore.
Not required for production use, only useful for manual inspection of file
restore VMs. Includes busybox and gdb.

View File

@ -0,0 +1 @@
build/initramfs/initramfs-debug.img /usr/lib/x86_64-linux-gnu/proxmox-backup/file-restore/

View File

@ -0,0 +1,2 @@
missing-depends-on-sensible-utils
uses-dpkg-database-directly

View File

@ -0,0 +1 @@
activate-noawait proxmox-backup-restore-image-update

View File

@ -9,12 +9,13 @@ SHIM_DIR=init-shim-rs
KERNEL_IMG=${BUILDDIR}/bzImage KERNEL_IMG=${BUILDDIR}/bzImage
INITRAMFS_IMG=${INITRAMFS_BUILDDIR}/initramfs.img INITRAMFS_IMG=${INITRAMFS_BUILDDIR}/initramfs.img
INITRAMFS_IMG_DBG=${INITRAMFS_BUILDDIR}/initramfs-debug.img
CONFIG=config-base CONFIG=config-base
RUST_SRC=$(wildcard ${SHIM_DIR}/**/*.rs) ${SHIM_DIR}/Cargo.toml RUST_SRC=$(wildcard ${SHIM_DIR}/**/*.rs) ${SHIM_DIR}/Cargo.toml
all: ${KERNEL_IMG} ${INITRAMFS_IMG} all: ${KERNEL_IMG} ${INITRAMFS_IMG_DBG}
${BUILDDIR}.prepared: ${CONFIG} ${BUILDDIR}.prepared: ${CONFIG}
rm -rf ${BUILDDIR} rm -rf ${BUILDDIR}
@ -54,6 +55,8 @@ ${INITRAMFS_IMG}: ${BUILDDIR}.prepared ${RUST_SRC} build_initramfs.sh
cd ${SHIM_DIR}; cargo build --release cd ${SHIM_DIR}; cargo build --release
sh build_initramfs.sh sh build_initramfs.sh
${INITRAMFS_IMG_DBG}: ${INITRAMFS_IMG}
.PHONY: test-run .PHONY: test-run
test-run: ${KERNEL_IMG} ${INITRAMFS_IMG} test-run: ${KERNEL_IMG} ${INITRAMFS_IMG}
# note: this will always fail since /proxmox-restore-daemon is not # note: this will always fail since /proxmox-restore-daemon is not

View File

@ -6,29 +6,36 @@ ROOT="root"
BUILDDIR="build/initramfs" BUILDDIR="build/initramfs"
INIT="../../init-shim-rs/target/release/init-shim-rs" INIT="../../init-shim-rs/target/release/init-shim-rs"
PKGS=" \
libc6:amd64 \
libgcc1:amd64 \
libstdc++6:amd64 \
libssl1.1:amd64 \
libattr1:amd64 \
libacl1:amd64
"
echo "Using build dir: $BUILDDIR" echo "Using build dir: $BUILDDIR"
rm -rf "$BUILDDIR" rm -rf "$BUILDDIR"
mkdir -p "$BUILDDIR" mkdir -p "$BUILDDIR"
cd "$BUILDDIR" cd "$BUILDDIR"
mkdir "$ROOT" mkdir "$ROOT"
# add necessary packages to initramfs # adds necessary packages to initramfs build root folder
for pkg in $PKGS; do add_pkgs() {
apt-get download "$pkg" DEPS=""
dpkg-deb -x ./*.deb "$ROOT" for pkg in $1; do
LOCAL_DEPS=$(apt-rdepends -f Depends -s Depends "$pkg" | grep -v '^ ')
DEPS="$DEPS $LOCAL_DEPS"
done
# debconf and gcc are unnecessary
DEPS=$(echo "$DEPS" |\
sed -E 's/debconf(-2\.0)?//' |\
sed -E 's/gcc-.{1,2}-base//')
apt-get download $DEPS
for deb in ./*.deb; do
dpkg-deb -x "$deb" "$ROOT"
done
rm ./*.deb rm ./*.deb
done }
rm -rf ${ROOT:?}/usr/share # contains only docs and debian stuff make_cpio() {
fakeroot -- sh -c "
cd '$ROOT';
find . -print0 | cpio --null -oV --format=newc -F ../$1
"
}
cp $INIT "$ROOT/init" cp $INIT "$ROOT/init"
chmod a+x "$ROOT/init" # just to be sure chmod a+x "$ROOT/init" # just to be sure
@ -36,7 +43,19 @@ chmod a+x "$ROOT/init" # just to be sure
# tell daemon it's running in the correct environment # tell daemon it's running in the correct environment
touch "$ROOT/restore-vm-marker" touch "$ROOT/restore-vm-marker"
fakeroot -- sh -c " add_pkgs "
cd '$ROOT'; libstdc++6:amd64 \
find . -print0 | cpio --null -oV --format=newc -F ../initramfs.img libssl1.1:amd64 \
libacl1:amd64 \
" "
rm -rf ${ROOT:?}/usr/share # contains only docs and debian stuff
make_cpio "initramfs.img"
# add debug helpers for debug initramfs, packages from above are included too
add_pkgs "
util-linux:amd64 \
busybox-static:amd64 \
gdb:amd64 \
"
# leave /usr/share here, it contains necessary stuff for gdb
make_cpio "initramfs-debug.img"

View File

@ -1,6 +1,8 @@
use anyhow::Error; use anyhow::{bail, Error};
use std::ffi::CStr; use std::ffi::CStr;
use std::fs; use std::fs;
use std::path::PathBuf;
use std::process::Command;
const URANDOM_MAJ: u64 = 1; const URANDOM_MAJ: u64 = 1;
const URANDOM_MIN: u64 = 9; const URANDOM_MIN: u64 = 9;
@ -21,15 +23,54 @@ fn main() {
do_mknod("/dev/urandom", URANDOM_MAJ, URANDOM_MIN) do_mknod("/dev/urandom", URANDOM_MAJ, URANDOM_MIN)
}); });
if let Err(err) = run_agetty() {
// not fatal
println!("[init-shim] debug: agetty start failed: {}", err);
}
let uptime = read_uptime(); let uptime = read_uptime();
println!("[init-shim] reached daemon start after {:.2}s", uptime); println!("[init-shim] reached daemon start after {:.2}s", uptime);
do_run("/proxmox-restore-daemon"); do_run("/proxmox-restore-daemon");
} }
fn run_agetty() -> Result<(), Error> {
use nix::unistd::{fork, ForkResult};
if !PathBuf::from("/sbin/agetty").exists() {
bail!("/sbin/agetty not found, probably not running debug mode and safe to ignore");
}
if !PathBuf::from("/sys/class/tty/ttyS1/device/driver/serial8250").exists() {
bail!("ttyS1 device does not exist or is not a 8250");
}
let dev = fs::read_to_string("/sys/class/tty/ttyS1/dev")?;
let (tty_maj, tty_min) = dev.trim().split_at(dev.find(':').unwrap_or(1));
do_mknod("/dev/ttyS1", tty_maj.parse()?, tty_min[1..].parse()?)?;
match unsafe { fork() } {
Ok(ForkResult::Parent { .. }) => {}
Ok(ForkResult::Child) => loop {
// continue to restart agetty if it exits, this runs in a forked process
println!("[init-shim] Spawning new agetty");
let res = Command::new("/sbin/agetty")
.args(&["-a", "root", "-l", "/bin/busybox", "-o", "sh", "115200", "ttyS1"])
.spawn()
.unwrap()
.wait()
.unwrap();
println!("[init-shim] agetty exited: {}", res.code().unwrap_or(-1));
},
Err(err) => println!("fork failed: {}", err),
}
Ok(())
}
fn do_mount(target: &str, fstype: &str) -> Result<(), Error> { fn do_mount(target: &str, fstype: &str) -> Result<(), Error> {
use nix::mount::{mount, MsFlags}; use nix::mount::{mount, MsFlags};
fs::create_dir(target)?; fs::create_dir_all(target)?;
let none_type: Option<&CStr> = None; let none_type: Option<&CStr> = None;
mount( mount(
none_type, none_type,
@ -63,7 +104,6 @@ fn read_uptime() -> f32 {
fn do_run(cmd: &str) -> ! { fn do_run(cmd: &str) -> ! {
use std::io::ErrorKind; use std::io::ErrorKind;
use std::process::Command;
let spawn_res = Command::new(cmd).env("RUST_BACKTRACE", "1").spawn(); let spawn_res = Command::new(cmd).env("RUST_BACKTRACE", "1").spawn();