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 }