github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/tools/testramfs/testramfs.go (about) 1 // Copyright 2012-2017 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // testramfs tests things, badly 6 package main 7 8 import ( 9 "io" 10 "log" 11 "os" 12 "syscall" 13 14 flag "github.com/spf13/pflag" 15 "golang.org/x/sys/unix" 16 17 "github.com/mvdan/u-root-coreutils/pkg/cpio" 18 "github.com/mvdan/u-root-coreutils/pkg/pty" 19 "github.com/mvdan/u-root-coreutils/pkg/termios" 20 ) 21 22 const ( 23 unshareFlags = syscall.CLONE_NEWNS 24 cloneFlags = syscall.CLONE_NEWIPC | 25 syscall.CLONE_NEWNET | 26 // making newpid work will be more tricky, 27 // since none of my CLs to fix go runtime for 28 // it ever got in. 29 // syscall.CLONE_NEWPID | 30 syscall.CLONE_NEWUTS | 31 0 32 ) 33 34 var ( 35 noremove = flag.BoolP("noremove", "n", false, "remove tempdir when done") 36 interactive = flag.BoolP("interactive", "i", false, "interactive mode") 37 ) 38 39 func main() { 40 flag.Parse() 41 42 if flag.NArg() != 1 { 43 log.Fatalf("usage: %s <cpio-path>", os.Args[0]) 44 } 45 46 c := flag.Args()[0] 47 48 f, err := os.Open(c) 49 if err != nil { 50 log.Fatal(err) 51 } 52 53 // So, what's the plan here? 54 // 55 // - new mount namespace 56 // - root mount is a tmpfs mount filled with the archive. 57 // 58 // - new PID namespace 59 // - archive/init actually runs as PID 1. 60 61 // Note this is basically a chroot and umask is inherited. 62 // The umask has to be zero else some creation will end 63 // up with incorrect permissions, a particular problem 64 // in device creation. 65 u := unix.Umask(0) 66 defer unix.Umask(u) 67 68 tempDir, err := os.MkdirTemp("", "u-root") 69 if err != nil { 70 log.Fatal(err) 71 } 72 // Don't do a RemoveAll. This should be empty and 73 // an error can tell us we got something wrong. 74 if !*noremove { 75 defer func(n string) { 76 log.Printf("Removing %v", n) 77 if err := os.Remove(n); err != nil { 78 log.Fatal(err) 79 } 80 }(tempDir) 81 } 82 if err := syscall.Mount("testramfs.tmpfs", tempDir, "tmpfs", 0, ""); err != nil { 83 log.Fatal(err) 84 } 85 if !*noremove { 86 defer func(n string) { 87 log.Printf("Unmounting %v", n) 88 if err := syscall.Unmount(n, syscall.MNT_DETACH); err != nil { 89 log.Fatal(err) 90 } 91 }(tempDir) 92 } 93 94 archiver, err := cpio.Format("newc") 95 if err != nil { 96 log.Fatal(err) 97 } 98 99 r := archiver.Reader(f) 100 for { 101 rec, err := r.ReadRecord() 102 if err == io.EOF { 103 break 104 } 105 if err != nil { 106 log.Fatal(err) 107 } 108 cpio.CreateFileInRoot(rec, tempDir, false) 109 } 110 111 cmd, err := pty.New() 112 if err != nil { 113 log.Fatal(err) 114 } 115 cmd.Command("/init") 116 cmd.C.SysProcAttr.Chroot = tempDir 117 cmd.C.SysProcAttr.Cloneflags = cloneFlags 118 cmd.C.SysProcAttr.Unshareflags = unshareFlags 119 if *interactive { 120 t, err := termios.GetTermios(0) 121 if err != nil { 122 log.Fatal("Getting Termios") 123 } 124 defer func(t *termios.Termios) { 125 if err := termios.SetTermios(0, t); err != nil { 126 log.Print(err) 127 } 128 }(t) 129 if err := cmd.Run(); err != nil { 130 log.Fatal(err) 131 } 132 return 133 } 134 if err := cmd.Start(); err != nil { 135 log.Fatal(err) 136 } 137 go io.Copy(cmd.TTY, cmd.Ptm) 138 139 // At this point you could use an array of commands/output templates to 140 // drive the test, and end with the exit command shown nere. 141 for _, c := range []string{"date\n", "exit\n", "exit\n", "exit\n"} { 142 if _, err := cmd.Ptm.Write([]byte(c)); err != nil { 143 log.Printf("Writing %s: %v", c, err) 144 } 145 } 146 if err := cmd.Wait(); err != nil { 147 log.Fatal(err) 148 } 149 }