github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/container/externalbuilder/session.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package externalbuilder 8 9 import ( 10 "bufio" 11 "io" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "sync" 16 "syscall" 17 18 "github.com/hechain20/hechain/common/flogging" 19 ) 20 21 type ExitFunc func(error) 22 23 type Session struct { 24 mutex sync.Mutex 25 command *exec.Cmd 26 exited chan struct{} 27 exitErr error 28 waitStatus syscall.WaitStatus 29 exitFuncs []ExitFunc 30 } 31 32 // Start will start the provided command and return a Session that can be used 33 // to await completion or signal the process. 34 // 35 // The provided logger is used log stderr from the running process. 36 func Start(logger *flogging.FabricLogger, cmd *exec.Cmd, exitFuncs ...ExitFunc) (*Session, error) { 37 logger = logger.With("command", filepath.Base(cmd.Path)) 38 39 stderr, err := cmd.StderrPipe() 40 if err != nil { 41 return nil, err 42 } 43 44 err = cmd.Start() 45 if err != nil { 46 return nil, err 47 } 48 49 sess := &Session{ 50 command: cmd, 51 exitFuncs: exitFuncs, 52 exited: make(chan struct{}), 53 } 54 go sess.waitForExit(logger, stderr) 55 56 return sess, nil 57 } 58 59 func (s *Session) waitForExit(logger *flogging.FabricLogger, stderr io.Reader) { 60 // copy stderr to the logger until stderr is closed 61 scanner := bufio.NewScanner(stderr) 62 for scanner.Scan() { 63 logger.Info(scanner.Text()) 64 } 65 if err := scanner.Err(); err != nil { 66 logger.Errorf("command output scanning failed: %s", err) 67 } 68 69 // wait for the command to exit and to complete 70 err := s.command.Wait() 71 72 // update state and close the exited channel 73 s.mutex.Lock() 74 defer s.mutex.Unlock() 75 s.exitErr = err 76 s.waitStatus = s.command.ProcessState.Sys().(syscall.WaitStatus) 77 for _, exit := range s.exitFuncs { 78 exit(s.exitErr) 79 } 80 close(s.exited) 81 } 82 83 // Wait waits for the running command to terminate and returns the exit error 84 // from the command. If the command has already exited, the exit err will be 85 // returned immediately. 86 func (s *Session) Wait() error { 87 <-s.exited 88 return s.exitErr 89 } 90 91 // Signal will send a signal to the running process. 92 func (s *Session) Signal(sig os.Signal) { 93 s.command.Process.Signal(sig) 94 }