github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/exp/builtin/builtin.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 package main 6 7 import ( 8 "flag" 9 "fmt" 10 "io/ioutil" 11 "log" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "syscall" 16 17 "golang.org/x/tools/imports" 18 ) 19 20 const ( 21 elvishPath = "/src/github.com/u-root/u-root/cmds/elvish" 22 ) 23 24 type mount struct { 25 source string 26 target string 27 fstype string 28 flags uintptr 29 opts string 30 } 31 32 var ( 33 startPart = "package main\n" 34 initPart = "func init() {\n addBuiltIn(\"%s\", %s)\n}\nfunc %s(c*Command) error {\nvar err error\n" 35 // endPart = "\n}\n)\n}\n" 36 endPart = "\nreturn err\n}\n" 37 namespace = []mount{ 38 {source: "tmpfs", target: elvishPath, fstype: "tmpfs", flags: syscall.MS_MGC_VAL, opts: ""}, 39 {source: "tmpfs", target: "/ubin", fstype: "tmpfs", flags: syscall.MS_MGC_VAL, opts: ""}, 40 } 41 debug = flag.Bool("d", false, "Print debug info") 42 ) 43 44 func main() { 45 opts := imports.Options{ 46 Fragment: true, 47 AllErrors: true, 48 Comments: true, 49 TabIndent: true, 50 TabWidth: 8, 51 } 52 flag.Parse() 53 a := flag.Args() 54 if len(a) < 2 || len(a)%2 != 0 { 55 log.Fatalf("Usage: builtin <command> <code> [<command> <code>]*") 56 } 57 filemap := make(map[string][]byte) 58 for ; len(a) > 0; a = a[2:] { 59 goCode := startPart 60 // Simple programs are just bits of code for main ... 61 if a[1][0] == '{' { 62 goCode = goCode + fmt.Sprintf(initPart, a[0], a[0], a[0]) 63 goCode = goCode + a[1][1:len(a[1])-1] 64 } else { 65 for _, v := range a[1:] { 66 if v == "{" { 67 goCode = goCode + fmt.Sprintf(initPart, a[0]) 68 continue 69 } 70 // FIXME: should only look for last arg. 71 if v == "}" { 72 break 73 } 74 goCode = goCode + v + "\n" 75 } 76 } 77 goCode = goCode + endPart 78 if *debug { 79 log.Printf("\n---------------------\n%v\n------------------------\n", goCode) 80 } 81 fullCode, err := imports.Process("commandline", []byte(goCode), &opts) 82 if err != nil { 83 log.Fatalf("bad parse: '%v': %v", goCode, err) 84 } 85 if *debug { 86 log.Printf("\n----FULLCODE---------\n%v\n------FULLCODE----------\n", string(fullCode)) 87 } 88 bName := filepath.Join(elvishPath, a[0]+".go") 89 filemap[bName] = fullCode 90 } 91 92 // processed code, read in shell files. 93 globs, err := filepath.Glob(elvishPath + "/*.go") 94 if err != nil { 95 log.Fatal(err) 96 } 97 for _, i := range globs { 98 if b, err := ioutil.ReadFile(i); err != nil { 99 log.Fatal(err) 100 } else { 101 if _, ok := filemap[i]; ok { 102 log.Fatalf("%v exists", i) 103 } 104 filemap[i] = b 105 } 106 } 107 108 if b, err := ioutil.ReadFile("/proc/mounts"); err == nil && false { 109 log.Printf("m %v\n", string(b)) 110 } 111 // we'd like to do this here, but it seems it doesn't end 112 // up applying to all procs in this group, leading to confusion. 113 // sometimes they get the private mount, sometimes not. 114 // It's a fundamental limit in the go runtime. 115 // So we hack it in the shell. 116 // There is no FIXME 117 if false { 118 if err := syscall.Unshare(syscall.CLONE_NEWNS); err != nil { 119 log.Fatal(err) 120 } 121 } 122 if *debug { 123 if b, err := ioutil.ReadFile("/proc/mounts"); err == nil { 124 log.Printf("Reading /proc/mount:m %v\n", b) 125 } 126 } 127 128 // We are rewriting the shell. We need to create a new binary, i.e. 129 // rewrite the one in /ubin. Sadly, there is no way to say "mount THIS bin 130 // before THAT bin". There will be ca. 3.18 and we might as well wait for 131 // that to become common. For now, we essentially erase /ubin but mounting 132 // a tmpfs over it. 133 // This would be infinitely easier with a true union file system. Oh well. 134 for _, m := range namespace { 135 if err := syscall.Mount(m.source, m.target, m.fstype, m.flags, m.opts); err != nil { 136 log.Printf("Mount :%s: on :%s: type :%s: flags %x: opts %v: %v\n", m.source, m.target, m.fstype, m.flags, m.opts, err) 137 } 138 } 139 // write the new elvishPath 140 for i, v := range filemap { 141 if err = ioutil.WriteFile(i, v, 0600); err != nil { 142 log.Fatal(err) 143 } 144 } 145 146 // the big fun: just run it. The Right Things Happen. 147 cmd := exec.Command("/buildbin/elvish") 148 cmd.Stdin = os.Stdin 149 cmd.Stderr = os.Stderr 150 cmd.Stdout = os.Stdout 151 // TODO: figure out why we get EPERM when we use this. 152 //cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true,} 153 if *debug { 154 log.Printf("Run %v", cmd) 155 } 156 if err := cmd.Run(); err != nil { 157 log.Printf("%v\n", err) 158 } 159 // Unshare doesn't work in a sane way due to a Go issue? 160 for _, m := range namespace { 161 if err := syscall.Unmount(m.target, syscall.MNT_FORCE); err != nil { 162 log.Printf("Umount :%s: %v\n", m.target, err) 163 } 164 } 165 log.Printf("builtin: /ubin/elvish returned!\n") 166 }