github.com/cloudfoundry-attic/garden-linux@v0.333.2-candidate/process_tracker/namespaced_signaller.go (about)

     1  package process_tracker
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"os/exec"
     8  	"path"
     9  	"path/filepath"
    10  
    11  	"time"
    12  
    13  	"github.com/cloudfoundry/gunk/command_runner"
    14  	"github.com/pivotal-golang/lager"
    15  )
    16  
    17  // Kills a process by invoking ./bin/wsh in the given container path using
    18  // a PID read from the given pidFile
    19  type NamespacedSignaller struct {
    20  	Runner        command_runner.CommandRunner
    21  	ContainerPath string
    22  	Logger        lager.Logger
    23  	Timeout       time.Duration
    24  }
    25  
    26  func (n *NamespacedSignaller) Signal(request *SignalRequest) error {
    27  	pidfile := path.Join(n.ContainerPath, "processes", fmt.Sprintf("%d.pid", request.Pid))
    28  
    29  	n.Logger.Debug("NamespacedSignaller.Signal-entered", lager.Data{"signal": request.Signal})
    30  	pid, err := PidFromFile(pidfile, n.Timeout)
    31  	if err != nil {
    32  		n.Logger.Error("NamespacedSignaller.Signal-failed-to-read-PID-file", err, lager.Data{"signal": request.Signal})
    33  		return err
    34  	}
    35  
    36  	cmd := exec.Command(filepath.Join(n.ContainerPath, "bin/wsh"),
    37  		"--socket", filepath.Join(n.ContainerPath, "run/wshd.sock"),
    38  		"--user", "root",
    39  		"kill", fmt.Sprintf("-%d", request.Signal), fmt.Sprintf("%d", pid))
    40  
    41  	n.Logger.Debug("NamespacedSignaller.Signal-about-to-run-kill-command", lager.Data{"signal": request.Signal, "cmd": cmd})
    42  	err = n.Runner.Run(cmd)
    43  	if err != nil {
    44  		n.Logger.Error("NamespacedSignaller.Signal-failed-to-run-kill", err, lager.Data{"signal": request.Signal})
    45  		return err
    46  	}
    47  
    48  	n.Logger.Debug("NamespacedSignaller.Signal-ran-kill-successfully", lager.Data{"signal": request.Signal})
    49  	return nil
    50  }
    51  
    52  func PidFromFile(pidFilePath string, timeout time.Duration) (int, error) {
    53  	pidFile, err := openPIDFile(pidFilePath, timeout)
    54  	if err != nil {
    55  		return 0, err
    56  	}
    57  	defer pidFile.Close()
    58  
    59  	fileContent, err := readPIDFile(pidFile, timeout)
    60  	if err != nil {
    61  		return 0, err
    62  	}
    63  
    64  	var pid int
    65  	_, err = fmt.Sscanf(fileContent, "%d", &pid)
    66  	if err != nil {
    67  		return 0, fmt.Errorf("linux_backend: can't parse PID file content: %v", err)
    68  	}
    69  
    70  	return pid, nil
    71  }
    72  
    73  func openPIDFile(pidFilePath string, timeout time.Duration) (*os.File, error) {
    74  	var err error
    75  
    76  	iterationCount := timeout / (time.Millisecond * 100)
    77  	for i := 0; i < int(iterationCount); i++ {
    78  		var pidFile *os.File
    79  		pidFile, err = os.Open(pidFilePath)
    80  		if err == nil {
    81  			return pidFile, nil
    82  		}
    83  		time.Sleep(time.Millisecond * 100)
    84  	}
    85  
    86  	return nil, fmt.Errorf("linux_backend: can't open PID file: %s", err)
    87  }
    88  
    89  func readPIDFile(pidFile *os.File, timeout time.Duration) (string, error) {
    90  	var bytesReadAmt int
    91  
    92  	buffer := make([]byte, 32)
    93  	iterationCount := timeout / (time.Millisecond * 100)
    94  	for i := 0; i < int(iterationCount); i++ {
    95  		bytesReadAmt, _ = pidFile.Read(buffer)
    96  
    97  		if bytesReadAmt == 0 {
    98  			pidFile.Seek(0, 0)
    99  			time.Sleep(time.Millisecond * 100)
   100  			continue
   101  		}
   102  		break
   103  	}
   104  
   105  	if bytesReadAmt == 0 {
   106  		return "", errors.New("linux_backend: can't read PID file: is empty or non existent")
   107  	}
   108  
   109  	return string(buffer), nil
   110  }