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  }