Files
podman/podman.go
2025-05-16 20:42:05 -07:00

157 lines
3.8 KiB
Go

// Binary podman is a gokrazy wrapper program that runs the bundled podman
// executable in /usr/local/bin/podman after doing any necessary runtime system
// setup.
package main
import (
"errors"
"fmt"
"io/fs"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"syscall"
"slices"
)
func isMounted(mountpoint string) (bool, error) {
b, err := ioutil.ReadFile("/proc/self/mountinfo")
if err != nil {
if os.IsNotExist(err) {
return false, nil // platform does not have /proc/self/mountinfo, fall back to not verifying
}
return false, err
}
for _, line := range strings.Split(strings.TrimSpace(string(b)), "\n") {
parts := strings.Fields(line)
if len(parts) < 5 {
continue
}
if filepath.Clean(parts[4]) == filepath.Clean(mountpoint) {
return true, nil
}
}
return false, nil
}
func makeWritable(dir string) error {
mounted, err := isMounted(dir)
if err != nil {
return err
}
if mounted {
// Nothing to do, directory is already mounted.
return nil
}
// Read all regular files in this directory.
regularFiles := make(map[string]string)
fis, err := os.ReadDir(dir)
if err != nil {
return err
}
for _, fi := range fis {
b, err := os.ReadFile(filepath.Join(dir, fi.Name()))
if err != nil {
return err
}
regularFiles[fi.Name()] = string(b)
}
if err := mount_tmpfs(dir); err != nil {
return err
}
// Write all regular files from memory back to new tmpfs.
for name, contents := range regularFiles {
if err := os.WriteFile(filepath.Join(dir, name), []byte(contents), 0644); err != nil {
return err
}
}
return nil
}
func mount_tmpfs(dest string) error {
if mounted, err := isMounted(dest); err == nil && mounted {
return nil
}
if err := syscall.Mount("tmpfs", dest, "tmpfs", syscall.MS_NOSUID|syscall.MS_RELATIME, "size=5G"); err != nil {
return fmt.Errorf("mounting tmpfs to %s: %v", dest, err)
}
return nil
}
// expandPath returns env, but with PATH= modified or added
// such that both /user and /usr/local/bin are included, which podman needs.
func expandPath(env []string) []string {
extra := []string{"/user", "/perm/bin", "/usr/local/bin"}
found := false
for idx, val := range env {
val, IsPATH := strings.CutPrefix(val, "PATH=")
if !IsPATH {
continue
}
paths := filepath.SplitList(val)
for i, path := range paths {
paths[i] = filepath.Clean(path)
}
for _, path := range extra {
if !slices.Contains(paths, path) {
paths = append(paths, path)
}
}
val = strings.Join(paths, ":")
env[idx] = "PATH=" + val
found = true
}
if !found {
const busyboxDefaultPATH = "/usr/local/sbin:/sbin:/usr/sbin:/bin:/usr/bin"
env = append(env, fmt.Sprintf("PATH=%s:%s", busyboxDefaultPATH, strings.Join(extra, ":")))
}
return env
}
func makeSymlinks() error {
if err := os.Symlink("/proc/self/fd/0", "/dev/stdin"); err != nil && !errors.Is(err, fs.ErrExist) {
return err
}
if err := os.Symlink("/proc/self/fd/1", "/dev/stdout"); err != nil && !errors.Is(err, fs.ErrExist) {
return err
}
if err := os.Symlink("/proc/self/fd/2", "/dev/stderr"); err != nil && !errors.Is(err, fs.ErrExist) {
return err
}
return nil
}
func main() {
// podman fails without these directories
if err := makeWritable("/etc/containers/networks/"); err != nil {
log.Fatal(err)
}
if err := os.MkdirAll("/var/lib/containers/storage/volumes", 0o777); err != nil {
log.Fatal(err)
}
// catatonit needs /var/tmp for the pause container
if err := os.MkdirAll("/var/tmp", 0o777); err != nil {
log.Fatal(err)
}
// podman default socket location is /run/podman/podman.sock
if err := os.MkdirAll("/run/podman/", 0o770); err != nil {
log.Fatal(err)
}
if err := makeSymlinks(); err != nil {
return
}
if err := syscall.Exec("/usr/local/bin/podman", os.Args, expandPath(os.Environ())); err != nil {
log.Fatal(err)
}
}