Skip to content

Commit

Permalink
升级前后的守护进程处理;优化寻找进程方法;Fix #15:Win覆盖文件名造成的守护进程判断异常
Browse files Browse the repository at this point in the history
  • Loading branch information
Karmenzind committed Jan 2, 2024
1 parent 1f59eff commit 70b79a0
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 76 deletions.
5 changes: 3 additions & 2 deletions cmd/kd.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ func flagUpdate(ctx *cli.Context, _ bool) (err error) {

if doUpdate {
emoji.Println(":lightning: Let's update now")
go daemon.KillDaemonIfRunning()
err = update.UpdateBinary(VERSION)
}
return err
Expand Down Expand Up @@ -162,7 +163,7 @@ func flagEditConfig(*cli.Context, bool) error {
}

func flagStatus(*cli.Context, bool) error {
di := internal.GetDaemonInfo()
di := daemon.GetDaemonInfo()
d.EchoRun("运行和相关配置信息如下:")
fmt.Printf(" Daemon端口:%s\n", di.Port)
fmt.Printf(" Daemon PID:%d\n", di.PID)
Expand Down Expand Up @@ -254,7 +255,7 @@ func main() {
}
}

if cfg.FileExists && cfg.ModTime > internal.GetDaemonInfo().StartTime {
if cfg.FileExists && cfg.ModTime > daemon.GetDaemonInfo().StartTime {
d.EchoWarn("检测到配置文件发生修改,正在重启守护进程")
flagStop(cCtx, true)
flagDaemon(cCtx, true)
Expand Down
41 changes: 37 additions & 4 deletions internal/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package internal

import (
"fmt"
"os"
"regexp"
"strings"

Expand All @@ -18,20 +19,52 @@ import (
"github.com/Karmenzind/kd/internal/daemon"
"github.com/Karmenzind/kd/internal/model"
q "github.com/Karmenzind/kd/internal/query"
"github.com/Karmenzind/kd/pkg"
d "github.com/Karmenzind/kd/pkg/decorate"
"github.com/Karmenzind/kd/pkg/proc"
"github.com/Karmenzind/kd/pkg/str"
"go.uber.org/zap"
)

func ensureDaemon(running chan bool) {
if !daemon.ServerIsRunning() {
err := daemon.StartDaemonProcess()
p, _ := daemon.FindServerProcess()
var err error
if p == nil {
d.EchoRun("未找到守护进程,正在启动...")
err = daemon.StartDaemonProcess()
if err != nil {
d.EchoRun("未找到守护进程,正在启动...")
d.EchoFatal(err.Error())
}
running <- true
} else {
exename, err := pkg.GetExecutablePath()
if err == nil {
runningExename, _ := p.Exe()
// TODO (k): <2024-01-03> 增加检查版本
if exename != runningExename {
d.EchoWarn(fmt.Sprintf("正在运行的守护程序(%s)与当前程序(%s)文件信息或版本不一致,将尝试重新启动守护进程", runningExename, exename))
err := proc.KillProcess(p)
if err != nil {
cmd := proc.GetKillCMD(p.Pid)
d.EchoError(fmt.Sprintf("停止进程%v失败,请手动执行:", p.Pid))
fmt.Println(cmd.String())
os.Exit(1)
}
d.EchoRun("已终止,正在启动新的守护进程...")
err = daemon.StartDaemonProcess()
if err != nil {
d.EchoFatal(err.Error())
}
}
}
}
// if !daemon.ServerIsRunning() {
// err := daemon.StartDaemonProcess()
// if err != nil {
// d.EchoRun("未找到守护进程,正在启动...")
// d.EchoFatal(err.Error())
// }
// running <- true
// }
running <- true
}

Expand Down
85 changes: 53 additions & 32 deletions internal/daemon/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,51 @@ package daemon

import (
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"syscall"
"time"

"github.com/Karmenzind/kd/internal/cache"
"github.com/Karmenzind/kd/pkg"
d "github.com/Karmenzind/kd/pkg/decorate"
"github.com/Karmenzind/kd/pkg/proc"

"github.com/shirou/gopsutil/v3/process"
"go.uber.org/zap"
)

type DaemonInfoType struct {
StartTime int64
Port string
PID int
}

var DaemonInfo = &DaemonInfoType{}

func RecordRunInfo(port string) {
DaemonInfo.StartTime = time.Now().Unix()
DaemonInfo.PID = os.Getpid()
DaemonInfo.Port = port
pkg.SaveJson(
filepath.Join(cache.CACHE_RUN_PATH, "daemon.json"),
DaemonInfo,
)
zap.S().Infof("Recorded running information of daemon %+v", DaemonInfo)
}

func GetDaemonInfo() *DaemonInfoType {
if *DaemonInfo == (DaemonInfoType{}) {
err := pkg.LoadJson(filepath.Join(cache.CACHE_RUN_PATH, "daemon.json"), DaemonInfo)
if err != nil {
d.EchoFatal("获取守护进程信息失败,请执行`kd --stop && kd --daemon`")
}
}
return DaemonInfo
}

func getKdPIDs() {
var cmd *exec.Cmd

Expand All @@ -40,13 +71,28 @@ func FindServerProcess() (*process.Process, error) {
if err != nil {
return nil, err
}
di := GetDaemonInfo()
for _, p := range processes {
// XXX err
n, _ := p.Name()
if p.Pid == int32(di.PID) {
zap.S().Debugf("Got daemon process %v via daemon info", di.PID)
cmdslice, _ := p.CmdlineSlice()
if len(cmdslice) > 1 && cmdslice[1] == "--server" {
return p, nil
}
}

if n == "kd" || (runtime.GOOS == "windows" && n == "kd.exe") {
cmd, _ := p.Cmdline()
if p.Pid == 13328 {
name, _ := p.Name()
cmdslice, _ := p.CmdlineSlice()
zap.S().Debugf("13328:Name: `%s` Cmd: `%s` cmdslice: `%+v`", name, cmd, cmdslice)
}
zap.S().Debugf("Found process kd.exe with CMD: %s", cmd)
if strings.Contains(cmd, " --server") {
zap.S().Debugf("Found process %+v Cmd: %s", p, cmd)
zap.S().Debugf("Found process %+v Cmd: `%s`", p, cmd)
return p, nil
}
}
Expand Down Expand Up @@ -91,43 +137,18 @@ func StartDaemonProcess() error {

func KillDaemonIfRunning() error {
p, err := FindServerProcess()
var trySysKill bool
if err == nil {
if p == nil {
d.EchoOkay("未发现守护进程,无需停止")
return nil
} else if runtime.GOOS != "windows" {
zap.S().Infof("Found running daemon PID: %d,", p.Pid)
errSig := p.SendSignal(syscall.SIGINT)
if errSig != nil {
zap.S().Warnf("Failed to stop PID %d with syscall.SIGINT: %s", p.Pid, errSig)
trySysKill = true
}
} else {
trySysKill = true
}
} else {
zap.S().Warnf("[process] Failed to find daemon: %s", err)
trySysKill = true
}
pidStr := strconv.Itoa(int(p.Pid))

if trySysKill {
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("taskkill", "/F", "/T", "/PID", pidStr)
// cmd = exec.Command("taskkill", "/im", "kd", "/T", "/F")
case "linux":
cmd = exec.Command("kill", "-9", pidStr)
// cmd = exec.Command("killall", "kd")
}
output, err := cmd.Output()
zap.S().Infof("Executed '%s'. Output %s", cmd, output)
if err != nil {
zap.S().Warnf("Failed to kill daemon with system command. Error: %s", output, err)
}
return err
}

err = proc.KillProcess(p)

if err == nil {
zap.S().Info("Terminated daemon process.")
d.EchoOkay("守护进程已经停止")
Expand Down
38 changes: 1 addition & 37 deletions internal/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,16 @@ import (
"fmt"
"io"
"net"
"os"
"path/filepath"
"time"

d "github.com/Karmenzind/kd/pkg/decorate"

"github.com/Karmenzind/kd/internal/cache"
"github.com/Karmenzind/kd/internal/daemon"
"github.com/Karmenzind/kd/internal/model"
"github.com/Karmenzind/kd/internal/query"
"github.com/Karmenzind/kd/pkg"
"go.uber.org/zap"
)

// TODO 支持自定义
var SERVER_PORT = 19707

type DaemonInfoType struct {
StartTime int64
Port string
PID int
}

var DaemonInfo = &DaemonInfoType{}

func recordRunningInfo(port string) {
DaemonInfo.StartTime = time.Now().Unix()
DaemonInfo.PID = os.Getpid()
DaemonInfo.Port = port
pkg.SaveJson(
filepath.Join(cache.CACHE_RUN_PATH, "daemon.json"),
DaemonInfo,
)
zap.S().Infof("Recorded running information of daemon %+v", DaemonInfo)
}

func GetDaemonInfo() *DaemonInfoType {
if *DaemonInfo == (DaemonInfoType{}) {
err := pkg.LoadJson(filepath.Join(cache.CACHE_RUN_PATH, "daemon.json"), DaemonInfo)
if err != nil {
d.EchoFatal("获取守护进程信息失败,请执行`kd --stop && kd --daemon`")
}
}
return DaemonInfo
}

func StartServer() (err error) {
IS_SERVER = true
addr := fmt.Sprintf("localhost:%d", SERVER_PORT)
Expand All @@ -67,7 +31,7 @@ func StartServer() (err error) {
return err
}
daemon.InitCron()
go recordRunningInfo(port)
go daemon.RecordRunInfo(port)

fmt.Printf("Listening on host: %s, port: %s\n", host, port)

Expand Down
51 changes: 51 additions & 0 deletions pkg/proc/proc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package proc

import (
"os/exec"
"runtime"
"strconv"
"syscall"

"github.com/shirou/gopsutil/v3/process"
"go.uber.org/zap"
)

// not for Win
func KillProcess(p *process.Process) (err error) {
if runtime.GOOS != "windows" {
errSig := p.SendSignal(syscall.SIGINT)
if errSig == nil {
if yes, errCheck := p.IsRunning(); errCheck == nil && !yes {
zap.S().Infof("Stopped process %v with SIGINT.", p.Pid)
return
}
} else {
zap.S().Warnf("Failed to stop PID %v with syscall.SIGINT: %s", p.Pid, errSig)
}
}
return SysKillPID(p.Pid)
}

func GetKillCMD(pid int32) *exec.Cmd {
pidStr := strconv.Itoa(int(pid))
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("taskkill", "/F", "/T", "/PID", pidStr)
// cmd = exec.Command("taskkill", "/im", "kd", "/T", "/F")
case "linux":
cmd = exec.Command("kill", "-9", pidStr)
// cmd = exec.Command("killall", "kd")
}
return cmd
}

func SysKillPID(pid int32) (err error) {
cmd := GetKillCMD(pid)
output, err := cmd.Output()
zap.S().Infof("Executed '%s'. Output %s", cmd, output)
if err != nil {
zap.S().Warnf("Failed to kill %v with system command. Output: `%s` Error: `%s`", pid, output, err)
}
return
}
2 changes: 1 addition & 1 deletion plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
# BUG

## Risk
- 实际文件名 不改的时候的process_name
- 实际文件名 不改的时候的process_name,增加同时校验kd和当前文件名

## low priority
- (action) linux 386
Expand Down

0 comments on commit 70b79a0

Please sign in to comment.