github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/exp/ash/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 rush(b *bufio.Reader) error { 194 foreground() 195 cmds, _, err := getCommand(b) 196 if err != nil { 197 return err 198 } 199 if err := doArgs(cmds); err != nil { 200 return err 201 } 202 if err := commands(cmds); err != nil { 203 return err 204 } 205 if err := wire(cmds); err != nil { 206 return err 207 } 208 for _, c := range cmds { 209 if err := command(c); err != nil { 210 fmt.Fprintf(os.Stderr, "%v\n", err) 211 if c.link == "||" { 212 continue 213 } 214 // yes, not needed, but useful so you know 215 // what goes on here. 216 if c.link == "&&" { 217 break 218 } 219 break 220 } else { 221 if c.link == "||" { 222 break 223 } 224 } 225 } 226 return nil 227 }