move initrd
This commit is contained in:
		| @ -1,190 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	yaml "gopkg.in/yaml.v2" | ||||
| 	"novit.nc/direktil/pkg/sysfs" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// VERSION is the current version of init | ||||
| 	VERSION = "Direktil init v0.1" | ||||
|  | ||||
| 	rootMountFlags  = 0 | ||||
| 	bootMountFlags  = syscall.MS_NOEXEC | syscall.MS_NODEV | syscall.MS_NOSUID | syscall.MS_RDONLY | ||||
| 	layerMountFlags = syscall.MS_RDONLY | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	bootVersion string | ||||
| ) | ||||
|  | ||||
| type config struct { | ||||
| 	Layers []string `yaml:"layers"` | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	log.Print("Welcome to ", VERSION) | ||||
|  | ||||
| 	// essential mounts | ||||
| 	mount("none", "/proc", "proc", 0, "") | ||||
| 	mount("none", "/sys", "sysfs", 0, "") | ||||
| 	mount("none", "/dev", "devtmpfs", 0, "") | ||||
|  | ||||
| 	// get the "boot version" | ||||
| 	bootVersion = param("version", "current") | ||||
| 	log.Printf("booting system %q", bootVersion) | ||||
|  | ||||
| 	// find and mount /boot | ||||
| 	bootMatch := param("boot", "") | ||||
| 	if bootMatch != "" { | ||||
| 		bootFS := param("boot.fs", "vfat") | ||||
| 		for i := 0; ; i++ { | ||||
| 			devNames := sysfs.DeviceByProperty("block", bootMatch) | ||||
|  | ||||
| 			if len(devNames) == 0 { | ||||
| 				if i > 30 { | ||||
| 					fatal("boot partition not found after 30s") | ||||
| 				} | ||||
| 				log.Print("boot partition not found, retrying") | ||||
| 				time.Sleep(1 * time.Second) | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			devFile := filepath.Join("/dev", devNames[0]) | ||||
|  | ||||
| 			log.Print("boot partition found: ", devFile) | ||||
|  | ||||
| 			mount(devFile, "/boot", bootFS, bootMountFlags, "") | ||||
| 			break | ||||
| 		} | ||||
| 	} else { | ||||
| 		log.Print("Assuming /boot is already populated.") | ||||
| 	} | ||||
|  | ||||
| 	// load config | ||||
| 	cfgPath := param("config", "/boot/config.yaml") | ||||
|  | ||||
| 	cfgBytes, err := ioutil.ReadFile(cfgPath) | ||||
| 	if err != nil { | ||||
| 		fatalf("failed to read %s: %v", cfgPath, err) | ||||
| 	} | ||||
|  | ||||
| 	cfg := &config{} | ||||
| 	if err := yaml.Unmarshal(cfgBytes, cfg); err != nil { | ||||
| 		fatal("failed to load config: ", err) | ||||
| 	} | ||||
|  | ||||
| 	// mount layers | ||||
| 	if len(cfg.Layers) == 0 { | ||||
| 		fatal("no layers configured!") | ||||
| 	} | ||||
|  | ||||
| 	log.Printf("wanted layers: %q", cfg.Layers) | ||||
|  | ||||
| 	lowers := make([]string, len(cfg.Layers)) | ||||
| 	for i, layer := range cfg.Layers { | ||||
| 		path := layerPath(layer) | ||||
|  | ||||
| 		info, err := os.Stat(path) | ||||
| 		if err != nil { | ||||
| 			fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		log.Printf("layer %s found (%d bytes)", layer, info.Size()) | ||||
|  | ||||
| 		dir := "/layers/" + layer | ||||
|  | ||||
| 		lowers[i] = dir | ||||
|  | ||||
| 		loopDev := fmt.Sprintf("/dev/loop%d", i) | ||||
| 		losetup(loopDev, path) | ||||
|  | ||||
| 		mount(loopDev, dir, "squashfs", layerMountFlags, "") | ||||
| 	} | ||||
|  | ||||
| 	// prepare system root | ||||
| 	mount("mem", "/changes", "tmpfs", 0, "") | ||||
|  | ||||
| 	mkdir("/changes/workdir", 0755) | ||||
| 	mkdir("/changes/upperdir", 0755) | ||||
|  | ||||
| 	mount("overlay", "/system", "overlay", rootMountFlags, | ||||
| 		"lowerdir="+strings.Join(lowers, ":")+",upperdir=/changes/upperdir,workdir=/changes/workdir") | ||||
| 	mount("/boot", "/system/boot", "", syscall.MS_BIND, "") | ||||
|  | ||||
| 	// - write configuration | ||||
| 	log.Print("writing /config.yaml") | ||||
| 	if err := ioutil.WriteFile("/system/config.yaml", cfgBytes, 0600); err != nil { | ||||
| 		fatal("failed: ", err) | ||||
| 	} | ||||
|  | ||||
| 	// clean zombies | ||||
| 	cleanZombies() | ||||
|  | ||||
| 	// switch root | ||||
| 	log.Print("switching root") | ||||
| 	err = syscall.Exec("/sbin/switch_root", []string{"switch_root", "/system", "/sbin/init"}, os.Environ()) | ||||
| 	if err != nil { | ||||
| 		log.Print("switch_root failed: ", err) | ||||
| 		select {} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func layerPath(name string) string { | ||||
| 	return fmt.Sprintf("/boot/%s/layers/%s.fs", bootVersion, name) | ||||
| } | ||||
|  | ||||
| func fatal(v ...interface{}) { | ||||
| 	log.Print("*** FATAL ***") | ||||
| 	log.Print(v...) | ||||
| 	dropToShell() | ||||
| } | ||||
|  | ||||
| func fatalf(pattern string, v ...interface{}) { | ||||
| 	log.Print("*** FATAL ***") | ||||
| 	log.Printf(pattern, v...) | ||||
| 	dropToShell() | ||||
| } | ||||
|  | ||||
| func dropToShell() { | ||||
| 	err := syscall.Exec("/bin/sh", []string{"/bin/sh"}, os.Environ()) | ||||
| 	log.Print("shell drop failed: ", err) | ||||
| 	select {} | ||||
| } | ||||
|  | ||||
| func losetup(dev, file string) { | ||||
| 	run("/sbin/losetup", dev, file) | ||||
| } | ||||
|  | ||||
| func run(cmd string, args ...string) { | ||||
| 	if output, err := exec.Command(cmd, args...).CombinedOutput(); err != nil { | ||||
| 		fatalf("command %s %q failed: %v\n%s", cmd, args, err, string(output)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func mkdir(dir string, mode os.FileMode) { | ||||
| 	if err := os.MkdirAll(dir, mode); err != nil { | ||||
| 		fatalf("mkdir %q failed: %v", dir, err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func mount(source, target, fstype string, flags uintptr, data string) { | ||||
| 	if _, err := os.Stat(target); os.IsNotExist(err) { | ||||
| 		mkdir(target, 0755) | ||||
| 	} | ||||
|  | ||||
| 	if err := syscall.Mount(source, target, fstype, flags, data); err != nil { | ||||
| 		fatalf("mount %q %q -t %q -o %q failed: %v", source, target, fstype, data, err) | ||||
| 	} | ||||
| 	log.Printf("mounted %q", target) | ||||
| } | ||||
| @ -1,23 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"io/ioutil" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| func param(name, defaultValue string) (value string) { | ||||
| 	ba, err := ioutil.ReadFile("/proc/cmdline") | ||||
| 	if err != nil { | ||||
| 		fatal("could not read /proc/cmdline: ", err) | ||||
| 	} | ||||
|  | ||||
| 	prefix := "direktil." + name + "=" | ||||
|  | ||||
| 	for _, part := range strings.Split(string(ba), " ") { | ||||
| 		if strings.HasPrefix(part, prefix) { | ||||
| 			return strings.TrimSpace(part[len(prefix):]) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return defaultValue | ||||
| } | ||||
| @ -1,24 +0,0 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
| 	"syscall" | ||||
| ) | ||||
|  | ||||
| func cleanZombies() { | ||||
| 	var wstatus syscall.WaitStatus | ||||
|  | ||||
| 	for { | ||||
| 		pid, err := syscall.Wait4(-1, &wstatus, 0, nil) | ||||
| 		switch err { | ||||
| 		case nil: | ||||
| 			log.Printf("collected PID %v", pid) | ||||
|  | ||||
| 		case syscall.ECHILD: | ||||
| 			return | ||||
|  | ||||
| 		default: | ||||
| 			log.Printf("unknown error: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user