diff options
author | Unknwon <u@gogs.io> | 2019-10-24 01:51:46 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-24 01:51:46 -0700 |
commit | 01c8df01ec0608f1f25b2f1444adabb98fa5ee8a (patch) | |
tree | f8a7e5dd8d2a8c51e1ce2cabb9d33571a93314dd /internal/process | |
parent | 613139e7bef81d3573e7988a47eb6765f3de347a (diff) |
internal: move packages under this directory (#5836)
* Rename pkg -> internal
* Rename routes -> route
* Move route -> internal/route
* Rename models -> db
* Move db -> internal/db
* Fix route2 -> route
* Move cmd -> internal/cmd
* Bump version
Diffstat (limited to 'internal/process')
-rw-r--r-- | internal/process/manager.go | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/internal/process/manager.go b/internal/process/manager.go new file mode 100644 index 00000000..babfceed --- /dev/null +++ b/internal/process/manager.go @@ -0,0 +1,140 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package process + +import ( + "bytes" + "errors" + "fmt" + "os/exec" + "sync" + "time" + + log "gopkg.in/clog.v1" +) + +var ( + ErrExecTimeout = errors.New("Process execution timeout") +) + +const DEFAULT_TIMEOUT = 60 * time.Second + +// Process represents a running process calls shell command. +type Process struct { + PID int64 + Description string + Start time.Time + Cmd *exec.Cmd +} + +type pidCounter struct { + sync.Mutex + + // The current number of pid, initial is 0, and increase 1 every time it's been used. + pid int64 +} + +func (c *pidCounter) PID() int64 { + c.pid++ + return c.pid +} + +var counter = new(pidCounter) +var Processes []*Process + +// Add adds a process to global list and returns its PID. +func Add(desc string, cmd *exec.Cmd) int64 { + counter.Lock() + defer counter.Unlock() + + pid := counter.PID() + Processes = append(Processes, &Process{ + PID: pid, + Description: desc, + Start: time.Now(), + Cmd: cmd, + }) + return pid +} + +// Remove removes a process from global list. +// It returns true if the process is found and removed by given pid. +func Remove(pid int64) bool { + counter.Lock() + defer counter.Unlock() + + for i := range Processes { + if Processes[i].PID == pid { + Processes = append(Processes[:i], Processes[i+1:]...) + return true + } + } + return false +} + +// Exec starts executing a shell command in given path, it tracks corresponding process and timeout. +func ExecDir(timeout time.Duration, dir, desc, cmdName string, args ...string) (string, string, error) { + if timeout == -1 { + timeout = DEFAULT_TIMEOUT + } + + bufOut := new(bytes.Buffer) + bufErr := new(bytes.Buffer) + + cmd := exec.Command(cmdName, args...) + cmd.Dir = dir + cmd.Stdout = bufOut + cmd.Stderr = bufErr + if err := cmd.Start(); err != nil { + return "", err.Error(), err + } + + pid := Add(desc, cmd) + done := make(chan error) + go func() { + done <- cmd.Wait() + }() + + var err error + select { + case <-time.After(timeout): + if errKill := Kill(pid); errKill != nil { + log.Error(2, "Fail to kill timeout process [pid: %d, desc: %s]: %v", pid, desc, errKill) + } + <-done + return "", ErrExecTimeout.Error(), ErrExecTimeout + case err = <-done: + } + + Remove(pid) + return bufOut.String(), bufErr.String(), err +} + +// Exec starts executing a shell command, it tracks corresponding process and timeout. +func ExecTimeout(timeout time.Duration, desc, cmdName string, args ...string) (string, string, error) { + return ExecDir(timeout, "", desc, cmdName, args...) +} + +// Exec starts executing a shell command, it tracks corresponding its process and use default timeout. +func Exec(desc, cmdName string, args ...string) (string, string, error) { + return ExecDir(-1, "", desc, cmdName, args...) +} + +// Kill kills and removes a process from global list. +func Kill(pid int64) error { + for _, proc := range Processes { + if proc.PID == pid { + if proc.Cmd != nil && proc.Cmd.Process != nil && + proc.Cmd.ProcessState != nil && !proc.Cmd.ProcessState.Exited() { + if err := proc.Cmd.Process.Kill(); err != nil { + return fmt.Errorf("fail to kill process [pid: %d, desc: %s]: %v", proc.PID, proc.Description, err) + } + } + Remove(pid) + return nil + } + } + return nil +} |