1// Copyright 2022 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package local 16 17import ( 18 "bufio" 19 "bytes" 20 "context" 21 "errors" 22 "io" 23 "os/exec" 24 "time" 25) 26 27// Run the input command via pipe with given arguments, stdout of the pipe is passed to input parser 28// argument. 29func runPipe(ctx context.Context, timeout time.Duration, cmdName string, args []string, parser func(r io.Reader)) (err error, stdErr string) { 30 ctx, cancel := context.WithTimeout(ctx, timeout) 31 defer cancel() 32 33 cmd := exec.CommandContext(ctx, cmdName, args[0:]...) 34 errorBuf := bytes.Buffer{} 35 cmd.Stderr = &errorBuf 36 stdout, err := cmd.StdoutPipe() 37 if err != nil { 38 return err, errorBuf.String() 39 } 40 41 if err = cmd.Start(); err != nil { 42 return err, errorBuf.String() 43 } 44 parser(stdout) 45 if err = cmd.Wait(); err != nil { 46 return err, errorBuf.String() 47 } 48 return nil, "" 49} 50 51// Run input command, stdout is passed via out parameter to user, if error the stderr is provided via 52// stdErr string to the user. 53func run(ctx context.Context, timeout time.Duration, cmdName string, args []string) (out *bytes.Buffer, err error, stdErr string) { 54 ctx, cancel := context.WithTimeout(ctx, timeout) 55 defer cancel() 56 57 cmd := exec.CommandContext(ctx, cmdName, args[0:]...) 58 errorBuf := bytes.Buffer{} 59 outputBuf := bytes.Buffer{} 60 cmd.Stderr = &errorBuf 61 cmd.Stdout = &outputBuf 62 if err = cmd.Run(); err != nil { 63 return nil, err, errorBuf.String() 64 } 65 66 return &outputBuf, nil, "" 67} 68 69// lineScanner 70// 71// Map output lines to strings, with expected number of 72// lines 73type lineScanner struct { 74 Lines []string 75} 76 77// Parse into lines 78func (l *lineScanner) Parse(s *bufio.Scanner) error { 79 i := 0 80 for s.Scan() { 81 if i < len(l.Lines) { 82 l.Lines[i] = s.Text() 83 } else { 84 i++ 85 break 86 } 87 i++ 88 } 89 if i != len(l.Lines) { 90 return errors.New("cmd: incorrect number of lines") 91 } 92 return nil 93} 94 95func newLineScanner(numLines int) *lineScanner { 96 out := &lineScanner{Lines: make([]string, numLines)} 97 return (out) 98} 99