internal/pid/pid.go (75 lines of code) (raw):

package pid import ( "fmt" "io/ioutil" "os" "os/exec" "strconv" "strings" "syscall" "github.com/go-kit/kit/log" "github.com/pkg/errors" ) const ( // chmod is used to set the mode bits for new seqnum files. chmod = os.FileMode(0600) ) // GetProcessStartTime returns the start time of the active process if still active func GetProcessStartTime(pid int) (string, error) { pidString := fmt.Sprintf("%d", pid) startTime, err := exec.Command("bash", "-c", "ps -o lstart= -p "+pidString).Output() if err != nil { return "", errors.Wrap(err, "failed to execute bash ps command") } return string(startTime), nil } // SaveCurrentPidAndStartTime stores current process id with start date in file extName.pid // Example: 325 Tue Dec 8 15:54:04 2020 func SaveCurrentPidAndStartTime(path string) error { pid := os.Getpid() pidString := fmt.Sprintf("%d", pid) startTime, err := GetProcessStartTime(pid) if err != nil { return errors.Wrap(err, "failed to execute bash ps command") } b := []byte(fmt.Sprintf("%s\t%s", pidString, startTime)) return errors.Wrap(os.WriteFile(path, b, chmod), "extName.pid: failed to write") } // DeleteCurrentPidAndStartTime delete the file created by SaveCurrentPidAndStartTime func DeleteCurrentPidAndStartTime(path string) error { return errors.Wrap(os.Remove(path), "failed to delete "+path) } // ReadPidAndStartTime reads the stored pid and process start time from a file extName.pid // Returns 0 and "" if path not found func ReadPidAndStartTime(path string) (int, string, error) { b, err := ioutil.ReadFile(path) if err != nil { if os.IsNotExist(err) { return 0, "", nil } return 0, "", errors.Wrap(err, "extName.pid: failed to read:"+path) } data := strings.Split(string(b), "\t") if len(data) != 2 { return 0, "", errors.Wrap(err, "unexpected format in extName.pid:"+string(b)) } pid, err := strconv.Atoi(data[0]) if err != nil { return 0, "", errors.Wrap(err, "failed to convert pid:"+data[0]) } return pid, data[1], nil } // IsExtensionStillRunning checks if there is active process for the same extension name func IsExtensionStillRunning(path string) bool { // Check if we have a file record for previous process previousPid, previousStartTime, err := ReadPidAndStartTime(path) if err != nil || previousPid == 0 || previousStartTime == "" { return false } // Try to get previous process start time startTime, err := GetProcessStartTime(previousPid) if err != nil || startTime == "" { return false } return startTime == previousStartTime } // KillPreviousExtension handles the case where a process for the same extension name is still active from previous execution. // We need to kill it before staring a new one. func KillPreviousExtension(ctx *log.Context, pidFilePath string) { if IsExtensionStillRunning(pidFilePath) { previousPid, _, _ := ReadPidAndStartTime(pidFilePath) if ctx != nil { ctx.Log("event", "check process", "Active previous execution found. Killing pid ", previousPid) } syscall.Kill(-previousPid, syscall.SIGKILL) // Negative pid means kill the whole process group DeleteCurrentPidAndStartTime(pidFilePath) } }