进程管理程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pmt/db/proc.go

280 lines
5.3 KiB

package db
import (
"errors"
"os"
"path/filepath"
"strconv"
"sync"
"syscall"
)
const (
StatusUnknown uint8 = iota // 未知状态
StatusStarting // 正在启动
StatusRunning // 正在运行
StatusStopping // 正在停止
StatusStopped // 已经停止
)
type Proc struct {
sync.RWMutex
tempDir string // 数据缓存目录
stdinFile string // 数据输入文件
stdoutFile string // 数据输出文件
stderrFile string // 错误输出文件
pidfile string // 记录PID的文件
starts int // 重启次数
status uint8 // 程序状态
pid int // 进程PID
app *App // 应用信息
process *os.Process // 系统进程
}
func NewProc(app *App) *Proc {
tempDir, err := TempDir(app.Name)
if err != nil {
panic(err)
}
proc := &Proc{
tempDir: tempDir,
stdinFile: filepath.Join(tempDir, "stdin.txt"),
stdoutFile: filepath.Join(tempDir, "stdout.txt"),
stderrFile: filepath.Join(tempDir, "stderr.txt"),
pidfile: filepath.Join(tempDir, "pid.txt"),
starts: app.StartCount,
status: app.Status,
pid: app.PID,
app: app,
process: nil,
}
inner, err := os.FindProcess(app.PID)
if err == nil && inner.Signal(syscall.Signal(0)) == nil {
proc.process = inner
}
return proc
}
// Start 启动进程
func (p *Proc) Start() error {
p.RLock()
if p.status != StatusUnknown && p.status != StatusStopped {
p.RUnlock()
return errors.New("process was running")
}
stdinFile := p.stdinFile
stdoutFile := p.stdoutFile
stderrFile := p.stderrFile
app := p.app
p.setStatus(StatusStarting)
p.RUnlock()
// 确定数据输入文件
stdin, err := GetFile(stdinFile)
if err != nil {
return err
}
if len(app.Stdin) > 0 {
err = WriteFile(stdinFile, []byte(app.Stdin))
if err != nil {
return err
}
}
// 确定数据输出文件
// TODO 监听内容输入,记录到数据库
stdout, err := GetFile(stdoutFile)
if err != nil {
return err
}
// 确定错误输出文件
// TODO 监听内容输入,记录到数据库
stderr, err := GetFile(stderrFile)
if err != nil {
return err
}
// 确定工作目录
cwd := app.Cwd
if len(cwd) == 0 {
cwd, _ = os.Getwd()
}
// 确定进程属性
attr := &os.ProcAttr{
Dir: cwd,
Env: os.Environ(),
Files: []*os.File{stdin, stdout, stderr},
//Sys: &syscall.SysProcAttr{HideWindow: true},
}
// 确定启动参数
var args []string
if len(app.InterpreterArgs) > 0 {
args = append(args, app.InterpreterArgs...)
}
if len(app.Script) > 0 {
args = append(args, app.Script)
}
if len(app.Command) > 0 {
args = append(args, app.Command...)
}
if len(app.Arguments) > 0 {
args = append(args, app.Arguments...)
}
// 启动进程
process, err := os.StartProcess(app.Interpreter, args, attr)
if err != nil {
return err
}
p.Lock()
defer p.Unlock()
p.process = process
p.pid = process.Pid
p.starts++
err = WriteFile(p.pidfile, []byte(strconv.Itoa(p.pid)))
if err != nil {
go p.Stop(true)
return err
}
err = UpdateApp(app.ID,
ConfigStatus(StatusRunning),
ConfigPid(process.Pid),
IncreaseStartCounter)
if err != nil {
go p.Stop(true)
return err
}
return nil
}
// Stop 停止进程
func (p *Proc) Stop(force ...bool) error {
p.Lock()
if p.process == nil {
p.Unlock()
return errors.New("process does not exist")
}
p.setStatus(StatusStopping)
p.Unlock()
// 确定是否优雅关停
gracefully := true
if len(force) > 0 {
gracefully = force[0] == false
}
p.Lock()
defer p.Unlock()
var err error
if gracefully {
err = p.process.Signal(syscall.SIGTERM)
} else {
err = p.process.Signal(syscall.SIGKILL)
p.process.Release()
}
p.setStatus(StatusStopped)
UpdateApp(p.app.ID, ConfigPid(-1), ConfigStatus(StatusStopped))
DeleteFile(p.pidfile)
return err
}
// Restart 重启进程
func (p *Proc) Restart(force ...bool) error {
// 如果进程可用则停止
if p.IsAlive() {
err := p.Stop(force...)
if err != nil {
return err
}
}
return p.Start()
}
// Wait 等待进程结束
func (p *Proc) Wait() (*os.ProcessState, error) {
p.RLock()
defer p.RUnlock()
if p.process == nil {
return nil, errors.New("process does not started")
}
return p.process.Wait()
}
func (p *Proc) setStatus(status uint8) {
UpdateApp(p.app.ID, ConfigStatus(status))
}
// IsAlive 判断进程是否激活
func (p *Proc) IsAlive() bool {
p.RLock()
pid := p.pid
p.RUnlock()
if proc, err := os.FindProcess(pid); err != nil {
return false
} else {
return proc.Signal(syscall.Signal(0)) == nil
}
}
// Release 释放进程资源
func (p *Proc) Release() error {
p.Lock()
defer p.Unlock()
var err error
if p.process != nil {
err = p.process.Release()
}
if de := DeleteFile(p.pidfile); de != nil {
return de
}
if de := DeleteFile(p.stdinFile); de != nil {
return de
}
if de := DeleteFile(p.stdoutFile); de != nil {
return de
}
if de := DeleteFile(p.stderrFile); de != nil {
return de
}
if de := DeleteFile(p.tempDir); de != nil {
return err
}
return err
}
// Status 获取运行状态
func (p *Proc) Status() uint8 {
p.RLock()
defer p.RUnlock()
return p.status
}
// Pid 获取进程PID
func (p *Proc) Pid() int {
p.RLock()
defer p.RUnlock()
return p.pid
}
func (p *Proc) NotifyStopped() {
p.Lock()
defer p.Unlock()
p.pid = -1
}