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