scan.go (2846B)
1 // Copyright 2023 The Go 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 /* 6 Package scan provides functionality for running govulncheck. 7 8 See [cmd/govulncheck/main.go] as a usage example. 9 10 [cmd/govulncheck/main.go]: https://go.googlesource.com/vuln/+/master/cmd/govulncheck/main.go 11 */ 12 package scan 13 14 import ( 15 "context" 16 "errors" 17 "io" 18 "os" 19 20 "golang.org/x/vuln/internal/scan" 21 ) 22 23 // Cmd represents an external govulncheck command being prepared or run, 24 // similar to exec.Cmd. 25 type Cmd struct { 26 // Stdin specifies the standard input. If provided, it is expected to be 27 // the output of govulncheck -json. 28 Stdin io.Reader 29 30 // Stdout specifies the standard output. If nil, Run connects os.Stdout. 31 Stdout io.Writer 32 33 // Stderr specifies the standard error. If nil, Run connects os.Stderr. 34 Stderr io.Writer 35 36 // Env is the environment to use. 37 // If Env is nil, the current environment is used. 38 // As in os/exec's Cmd, only the last value in the slice for 39 // each environment key is used. To specify the setting of only 40 // a few variables, append to the current environment, as in: 41 // 42 // opt.Env = append(os.Environ(), "GOOS=plan9", "GOARCH=386") 43 // 44 Env []string 45 46 ctx context.Context 47 args []string 48 done chan struct{} 49 err error 50 } 51 52 // Command returns the Cmd struct to execute govulncheck with the given 53 // arguments. 54 func Command(ctx context.Context, arg ...string) *Cmd { 55 return &Cmd{ 56 ctx: ctx, 57 args: arg, 58 } 59 } 60 61 // Start starts the specified command but does not wait for it to complete. 62 // 63 // After a successful call to Start the Wait method must be called in order to 64 // release associated system resources. 65 func (c *Cmd) Start() error { 66 if c.done != nil { 67 return errors.New("vuln: already started") 68 } 69 if c.Stdin == nil { 70 c.Stdin = os.Stdin 71 } 72 if c.Stdout == nil { 73 c.Stdout = os.Stdout 74 } 75 if c.Stderr == nil { 76 c.Stderr = os.Stderr 77 } 78 if c.Env == nil { 79 c.Env = os.Environ() 80 } 81 c.done = make(chan struct{}) 82 go func() { 83 defer close(c.done) 84 c.err = c.scan() 85 }() 86 return nil 87 } 88 89 // Wait waits for the command to exit. The command must have been started by 90 // Start. 91 // 92 // If the command fails to run or does not complete successfully, the returned 93 // error wraps an error implementing: 94 // 95 // interface { 96 // ExitCode() int 97 // } 98 // 99 // Callers can use errors.As to retrieve the exit code. Other errors may be 100 // returned for other problems (e.g. I/O issues). 101 // 102 // Wait releases any resources associated with the Cmd. 103 func (c *Cmd) Wait() error { 104 if c.done == nil { 105 return errors.New("vuln: start must be called before wait") 106 } 107 <-c.done 108 return c.err 109 } 110 111 func (c *Cmd) scan() error { 112 if err := c.ctx.Err(); err != nil { 113 return err 114 } 115 return scan.RunGovulncheck(c.ctx, c.Env, c.Stdin, c.Stdout, c.Stderr, c.args) 116 }