initrd/main.go
2022-02-04 21:45:18 +01:00

155 lines
2.9 KiB
Go

package main
import (
"fmt"
"io"
"log"
"os"
"os/exec"
"runtime"
"syscall"
"time"
"golang.org/x/term"
)
const (
// VERSION is the current version of init
VERSION = "Direktil init v2.0"
rootMountFlags = 0
bootMountFlags = syscall.MS_NOEXEC | syscall.MS_NODEV | syscall.MS_NOSUID | syscall.MS_RDONLY
layerMountFlags = syscall.MS_RDONLY
)
var (
bootVersion string
)
func main() {
runtime.LockOSThread()
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)
_, err := os.Stat("/config.yaml")
if err != nil {
if os.IsNotExist(err) {
bootV1()
return
}
fatal("stat failed: ", err)
}
bootV2()
}
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...)
die()
}
func fatalf(pattern string, v ...interface{}) {
log.Print("*** FATAL ***")
log.Printf(pattern, v...)
die()
}
func die() {
fmt.Println("\nwill reboot in 1 minute; press r to reboot now, o to power off, s to get a shell")
deadline := time.Now().Add(time.Minute)
term.MakeRaw(int(os.Stdin.Fd())) // disable line buffering
os.Stdin.SetReadDeadline(deadline)
b := []byte{0}
for {
_, err := os.Stdin.Read(b)
if err != nil {
break
}
fmt.Println(string(b))
switch b[0] {
case 'o':
syscall.Reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
case 'r':
syscall.Reboot(syscall.LINUX_REBOOT_CMD_RESTART)
case 's':
err = syscall.Exec("/bin/ash", []string{"/bin/ash"}, os.Environ())
if err != nil {
fmt.Println("failed to start the shell:", err)
}
}
}
syscall.Reboot(syscall.LINUX_REBOOT_CMD_RESTART)
}
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)
}
func cp(srcPath, dstPath string) {
var err error
defer func() {
if err != nil {
fatalf("cp %s %s failed: %v", srcPath, dstPath, err)
}
}()
src, err := os.Open(srcPath)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstPath)
if err != nil {
return
}
defer dst.Close()
_, err = io.Copy(dst, src)
}