1// Copyright 2019 The ChromiumOS Authors 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package main 6 7import ( 8 "bytes" 9 "fmt" 10 "io" 11 "os" 12 "strings" 13 "syscall" 14 "time" 15) 16 17type env interface { 18 umask(int) int 19 getenv(key string) (string, bool) 20 environ() []string 21 getwd() string 22 stdin() io.Reader 23 stdout() io.Writer 24 stderr() io.Writer 25 run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error 26 runWithTimeout(cmd *command, duration time.Duration) error 27 exec(cmd *command) error 28} 29 30type processEnv struct { 31 wd string 32} 33 34func newProcessEnv() (env, error) { 35 wd, err := os.Getwd() 36 if err != nil { 37 return nil, wrapErrorwithSourceLocf(err, "failed to read working directory") 38 } 39 40 // Note: On Linux, Getwd may resolve to /proc/self/cwd, since it checks the PWD environment 41 // variable. We need to read the link to get the actual working directory. We can't always 42 // do this as we are calculating the path to clang, since following a symlinked cwd first 43 // would make this calculation invalid. 44 // 45 // FIXME(gbiv): It's not clear why always Readlink()ing here an issue. crrev.com/c/1764624 46 // might provide helpful context? 47 if wd == "/proc/self/cwd" { 48 wd, err = os.Readlink(wd) 49 if err != nil { 50 return nil, wrapErrorwithSourceLocf(err, "resolving /proc/self/cwd") 51 } 52 } 53 54 return &processEnv{wd: wd}, nil 55} 56 57var _ env = (*processEnv)(nil) 58 59func (env *processEnv) umask(newmask int) (oldmask int) { 60 return syscall.Umask(newmask) 61} 62 63func (env *processEnv) getenv(key string) (string, bool) { 64 return os.LookupEnv(key) 65} 66 67func (env *processEnv) environ() []string { 68 return os.Environ() 69} 70 71func (env *processEnv) getwd() string { 72 return env.wd 73} 74 75func (env *processEnv) stdin() io.Reader { 76 return os.Stdin 77} 78 79func (env *processEnv) stdout() io.Writer { 80 return os.Stdout 81} 82 83func (env *processEnv) stderr() io.Writer { 84 return os.Stderr 85} 86 87func (env *processEnv) exec(cmd *command) error { 88 return execCmd(env, cmd) 89} 90 91func (env *processEnv) runWithTimeout(cmd *command, duration time.Duration) error { 92 return runCmdWithTimeout(env, cmd, duration) 93} 94 95func (env *processEnv) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { 96 return runCmd(env, cmd, stdin, stdout, stderr) 97} 98 99type commandRecordingEnv struct { 100 env 101 stdinReader io.Reader 102 cmdResults []*commandResult 103} 104type commandResult struct { 105 Cmd *command `json:"cmd"` 106 Stdout string `json:"stdout,omitempty"` 107 Stderr string `json:"stderr,omitempty"` 108 ExitCode int `json:"exitcode,omitempty"` 109} 110 111var _ env = (*commandRecordingEnv)(nil) 112 113func (env *commandRecordingEnv) stdin() io.Reader { 114 return env.stdinReader 115} 116 117func (env *commandRecordingEnv) exec(cmd *command) error { 118 // Note: We treat exec the same as run so that we can do work 119 // after the call. 120 return env.run(cmd, env.stdin(), env.stdout(), env.stderr()) 121} 122 123func (env *commandRecordingEnv) runWithTimeout(cmd *command, duration time.Duration) error { 124 return env.runWithTimeout(cmd, duration) 125} 126 127func (env *commandRecordingEnv) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { 128 stdoutBuffer := &bytes.Buffer{} 129 stderrBuffer := &bytes.Buffer{} 130 err := env.env.run(cmd, stdin, io.MultiWriter(stdout, stdoutBuffer), io.MultiWriter(stderr, stderrBuffer)) 131 if exitCode, ok := getExitCode(err); ok { 132 env.cmdResults = append(env.cmdResults, &commandResult{ 133 Cmd: cmd, 134 Stdout: stdoutBuffer.String(), 135 Stderr: stderrBuffer.String(), 136 ExitCode: exitCode, 137 }) 138 } 139 return err 140} 141 142type printingEnv struct { 143 env 144} 145 146var _env = (*printingEnv)(nil) 147 148func (env *printingEnv) exec(cmd *command) error { 149 printCmd(env, cmd) 150 return env.env.exec(cmd) 151} 152 153func (env *printingEnv) runWithTimeout(cmd *command, duration time.Duration) error { 154 printCmd(env, cmd) 155 return env.env.runWithTimeout(cmd, duration) 156} 157 158func (env *printingEnv) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { 159 printCmd(env, cmd) 160 return env.env.run(cmd, stdin, stdout, stderr) 161} 162 163func printCmd(env env, cmd *command) { 164 fmt.Fprintf(env.stderr(), "cd '%s' &&", env.getwd()) 165 if len(cmd.EnvUpdates) > 0 { 166 fmt.Fprintf(env.stderr(), " env '%s'", strings.Join(cmd.EnvUpdates, "' '")) 167 } 168 fmt.Fprintf(env.stderr(), " '%s'", getAbsCmdPath(env, cmd)) 169 if len(cmd.Args) > 0 { 170 fmt.Fprintf(env.stderr(), " '%s'", strings.Join(cmd.Args, "' '")) 171 } 172 io.WriteString(env.stderr(), "\n") 173} 174