github.com/ahlemtn/fabric@v2.1.1+incompatible/core/container/externalbuilder/session.go (about) 1 /* 2 Copyright IBM Corp. 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/hyperledger/fabric/common/flogging" 19 ) 20 21 type Session struct { 22 mutex sync.Mutex 23 command *exec.Cmd 24 exited chan struct{} 25 exitErr error 26 waitStatus syscall.WaitStatus 27 } 28 29 // Start will start the provided command and return a Session that can be used 30 // to await completion or signal the process. 31 // 32 // The provided logger is used log stderr from the running process. 33 func Start(logger *flogging.FabricLogger, cmd *exec.Cmd) (*Session, error) { 34 logger = logger.With("command", filepath.Base(cmd.Path)) 35 36 stderr, err := cmd.StderrPipe() 37 if err != nil { 38 return nil, err 39 } 40 41 err = cmd.Start() 42 if err != nil { 43 return nil, err 44 } 45 46 sess := &Session{ 47 command: cmd, 48 exited: make(chan struct{}), 49 } 50 go sess.waitForExit(logger, stderr) 51 52 return sess, nil 53 } 54 55 func (s *Session) waitForExit(logger *flogging.FabricLogger, stderr io.Reader) { 56 // copy stderr to the logger until stderr is closed 57 scanner := bufio.NewScanner(stderr) 58 for scanner.Scan() { 59 logger.Info(scanner.Text()) 60 } 61 if err := scanner.Err(); err != nil { 62 logger.Errorf("command output scanning failed: %s", err) 63 } 64 65 // wait for the command to exit and to complete 66 err := s.command.Wait() 67 68 // update state and close the exited channel 69 s.mutex.Lock() 70 defer s.mutex.Unlock() 71 s.exitErr = err 72 s.waitStatus = s.command.ProcessState.Sys().(syscall.WaitStatus) 73 close(s.exited) 74 } 75 76 // Wait waits for the running command to terminate and returns the exit error 77 // from the command. If the command has already exited, the exit err will be 78 // returned immediately. 79 func (s *Session) Wait() error { 80 <-s.exited 81 return s.exitErr 82 } 83 84 // Signal will send a signal to the running process. 85 func (s *Session) Signal(sig os.Signal) { 86 s.command.Process.Signal(sig) 87 }