From c9528b4abb0249868978c4ad257aa88761e3519a Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 31 Dec 2024 09:50:32 +0100 Subject: [PATCH] try to install busybox into a tmpfs /bin (with fallback) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This code path requires gokrazy/tools at this commit or newer: https://github.com/gokrazy/tools/commit/37e2f95c5cfc58554405cc615c5da8e4899b071a And gokrazy/serial-breakglass at this commit or newer: https://github.com/gokrazy/serial-busybox/commit/bf9bc192356e3762c3d4e99c037d0e868d50d313 Afterwards, with an ~/.ssh/config entry like this: Host scan2drive ProxyCommand breakglass -proxy %h …using Emacs TRAMP should just work: emacs /ssh:scan2drive:/perm/keep/index.md --- busybox.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ ssh.go | 10 ++++++++++ 2 files changed, 63 insertions(+) create mode 100644 busybox.go diff --git a/busybox.go b/busybox.go new file mode 100644 index 0000000..d395380 --- /dev/null +++ b/busybox.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + "log" + "os" + "os/exec" + "strings" + "syscall" +) + +const wellKnownBusybox = "/usr/local/bin/busybox" + +// mountBin bind-mounts /bin to a tmpfs. +func mountBin() error { + b, err := os.ReadFile("/proc/self/mountinfo") + if err != nil { + return err + } + for _, line := range strings.Split(strings.TrimSpace(string(b)), "\n") { + parts := strings.Fields(line) + if len(parts) < 5 { + continue + } + mountpoint := parts[4] + log.Printf("Found mountpoint %q", parts[4]) + if mountpoint == "/bin" { + log.Printf("/bin file system already mounted, nothing to do") + return nil + } + } + + if err := syscall.Mount("tmpfs", "/bin", "tmpfs", 0, ""); err != nil { + return fmt.Errorf("mounting tmpfs on /bin: %v", err) + } + + return nil +} + +func installBusybox() error { + // /bin is read-only by default, so mount a tmpfs over it + if err := mountBin(); err != nil { + return err + } + + install := exec.Command(wellKnownBusybox, "--install", "-s", "/bin") + install.Stdout = os.Stdout + install.Stderr = os.Stderr + if err := install.Run(); err != nil { + return fmt.Errorf("%v: %v", install.Args, err) + } + return nil +} diff --git a/ssh.go b/ssh.go index bd55ed1..fdaa859 100644 --- a/ssh.go +++ b/ssh.go @@ -215,6 +215,16 @@ type exitStatus struct { } func findShell() string { + if _, err := os.Stat(wellKnownBusybox); err == nil { + // Install busybox to /bin to provide the typical userspace utilities + // in standard locations (makes Emacs TRAMP work, for example). + if err := installBusybox(); err != nil { + log.Printf("installing busybox failed: %v", err) + // fallthrough + } else { + return "/bin/sh" // available after installation + } + } if path, err := exec.LookPath("sh"); err == nil { return path }