github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/cmds/rush/rush.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 // Rush is an interactive shell similar to sh. 6 // 7 // Description: 8 // Prompt is '% '. 9 package main 10 11 import ( 12 "bufio" 13 "fmt" 14 "io" 15 "io/ioutil" 16 "log" 17 "os" 18 "os/exec" 19 "path" 20 "path/filepath" 21 "syscall" 22 ) 23 24 type builtin func(c *Command) error 25 26 // TODO: probably have one builtin map and use it for both types? 27 var ( 28 urpath = "/go/bin:/ubin:/buildbin:/bbin:/bin:/usr/local/bin:" 29 builtins = make(map[string]builtin) 30 // Some builtins really want to be forked off, esp. in the busybox case. 31 forkBuiltins = make(map[string]builtin) 32 // the environment dir is INTENDED to be per-user and bound in 33 // a private name space at /env. 34 envDir = "/env" 35 ) 36 37 func addBuiltIn(name string, f builtin) error { 38 if _, ok := builtins[name]; ok { 39 return fmt.Errorf("%v already a builtin", name) 40 } 41 builtins[name] = f 42 return nil 43 } 44 45 func addForkBuiltIn(name string, f builtin) error { 46 if _, ok := builtins[name]; ok { 47 return fmt.Errorf("%v already a forkBuiltin", name) 48 } 49 forkBuiltins[name] = f 50 return nil 51 } 52 53 func wire(cmds []*Command) error { 54 for i, c := range cmds { 55 // IO defaults. 56 var err error 57 if c.Stdin == nil { 58 if c.Stdin, err = openRead(c, os.Stdin, 0); err != nil { 59 return err 60 } 61 } 62 if c.link != "|" { 63 if c.Stdout, err = openWrite(c, os.Stdout, 1); err != nil { 64 return err 65 } 66 } 67 if c.Stderr, err = openWrite(c, os.Stderr, 2); err != nil { 68 return err 69 } 70 // The validation is such that "|" is not set on the last one. 71 // Also, there won't be redirects and "|" inappropriately. 72 if c.link != "|" { 73 continue 74 } 75 w, err := cmds[i+1].StdinPipe() 76 if err != nil { 77 return err 78 } 79 r, err := cmds[i].StdoutPipe() 80 if err != nil { 81 return err 82 } 83 // Oh, yuck. 84 // There seems to be no way to do the classic 85 // inherited pipes thing in Go. Hard to believe. 86 go func() { 87 io.Copy(w, r) 88 w.Close() 89 }() 90 } 91 return nil 92 } 93 94 func runit(c *Command) error { 95 defer func() { 96 for fd, f := range c.files { 97 f.Close() 98 delete(c.files, fd) 99 } 100 }() 101 if b, ok := builtins[c.cmd]; ok { 102 if err := b(c); err != nil { 103 return err 104 } 105 } else { 106 c.Cmd.SysProcAttr = &syscall.SysProcAttr{} 107 if c.bg { 108 c.Cmd.SysProcAttr.Setpgid = true 109 } else { 110 c.Cmd.SysProcAttr.Foreground = true 111 c.Cmd.SysProcAttr.Ctty = int(ttyf.Fd()) 112 } 113 if err := c.Start(); err != nil { 114 return fmt.Errorf("%v: Path %v", err, os.Getenv("PATH")) 115 } 116 if err := c.Wait(); err != nil { 117 return fmt.Errorf("wait: %v", err) 118 } 119 } 120 return nil 121 } 122 123 func openRead(c *Command, r io.Reader, fd int) (io.Reader, error) { 124 if c.fdmap[fd] != "" { 125 f, err := os.Open(c.fdmap[fd]) 126 c.files[fd] = f 127 return f, err 128 } 129 return r, nil 130 } 131 132 func openWrite(c *Command, w io.Writer, fd int) (io.Writer, error) { 133 if c.fdmap[fd] != "" { 134 f, err := os.Create(c.fdmap[fd]) 135 c.files[fd] = f 136 return f, err 137 } 138 return w, nil 139 } 140 141 func doArgs(cmds []*Command) error { 142 for _, c := range cmds { 143 globargv := []string{} 144 for _, v := range c.args { 145 if v.mod == "ENV" { 146 e := v.val 147 if !path.IsAbs(v.val) { 148 e = filepath.Join(envDir, e) 149 } 150 b, err := ioutil.ReadFile(e) 151 if err != nil { 152 return err 153 } 154 // It goes in as one argument. Not sure if this is what we want 155 // but it gets very weird to start splitting it on spaces. Or maybe not? 156 globargv = append(globargv, string(b)) 157 } else if globs, err := filepath.Glob(v.val); err == nil && len(globs) > 0 { 158 globargv = append(globargv, globs...) 159 } else { 160 globargv = append(globargv, v.val) 161 } 162 } 163 164 c.cmd = globargv[0] 165 c.argv = globargv[1:] 166 } 167 return nil 168 } 169 170 // There seems to be no harm in creating a Cmd struct 171 // even for builtins, so for now, we do. 172 // It will, however, do a path lookup, which we really don't need, 173 // and we may change it later. 174 func commands(cmds []*Command) error { 175 for _, c := range cmds { 176 c.Cmd = exec.Command(c.cmd, c.argv[:]...) 177 // this is a Very Special Case related to a Go issue. 178 // we're not able to unshare correctly in builtin. 179 // Not sure of the issue but this hack will have to do until 180 // we understand it. Barf. 181 if c.cmd == "builtin" { 182 c.Cmd.SysProcAttr.Cloneflags |= syscall.CLONE_NEWNS 183 } 184 } 185 return nil 186 } 187 func command(c *Command) error { 188 // for now, bg will just happen in background. 189 if c.bg { 190 go func() { 191 if err := runit(c); err != nil { 192 fmt.Fprintf(os.Stderr, "%v", err) 193 } 194 }() 195 } else { 196 err := runit(c) 197 return err 198 } 199 return nil 200 } 201 202 func main() { 203 b := bufio.NewReader(os.Stdin) 204 205 // we use path.Base in case they type something like ./cmd 206 if f, ok := forkBuiltins[path.Base(os.Args[0])]; ok { 207 if err := f(&Command{cmd: os.Args[0], Cmd: &exec.Cmd{Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr}, argv: os.Args[1:]}); err != nil { 208 log.Fatalf("%v", err) 209 } 210 os.Exit(0) 211 } 212 213 if len(os.Args) != 1 { 214 fmt.Println("no scripts/args yet") 215 os.Exit(1) 216 } 217 218 tty() 219 fmt.Printf("%% ") 220 for { 221 foreground() 222 cmds, status, err := getCommand(b) 223 if err != nil { 224 fmt.Fprintf(os.Stderr, "%v\n", err) 225 } 226 if err := doArgs(cmds); err != nil { 227 fmt.Fprintf(os.Stderr, "args problem: %v\n", err) 228 continue 229 } 230 if err := commands(cmds); err != nil { 231 fmt.Fprintf(os.Stderr, "%v\n", err) 232 continue 233 } 234 if err := wire(cmds); err != nil { 235 fmt.Fprintf(os.Stderr, "%v\n", err) 236 continue 237 } 238 for _, c := range cmds { 239 if err := command(c); err != nil { 240 fmt.Fprintf(os.Stderr, "%v\n", err) 241 if c.link == "||" { 242 continue 243 } 244 // yes, not needed, but useful so you know 245 // what goes on here. 246 if c.link == "&&" { 247 break 248 } 249 break 250 } else { 251 if c.link == "||" { 252 break 253 } 254 } 255 } 256 if status == "EOF" { 257 break 258 } 259 fmt.Printf("%% ") 260 } 261 }