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  }