• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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