Skip to content

Commit

Permalink
💊 added systemd support
Browse files Browse the repository at this point in the history
  • Loading branch information
Karmenzind committed May 5, 2024
1 parent e4d6fcb commit 500bafb
Show file tree
Hide file tree
Showing 11 changed files with 177 additions and 77 deletions.
22 changes: 4 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,13 @@ Invoke-WebRequest -uri 'https://github.com/Karmenzind/kd/releases/latest/downloa
### 卸载

<details><summary>🖱️ 点击展开</summary><pre>

1. 删除kd可执行文件(Linux/Mac:/usr/local/bin/kd,Win:C:\bin\kd.exe)
2. 删除配置文件和缓存目录
- Linux: `rm -rfv ~/.config/kd.toml ~/.cache/kdcache`
- MacOS: `rm -rfv ~/.config/kd.toml ~/Library/Caches/kdcache`
- Win: `rm ~\kd.toml ~\kdcache`

如果通过AUR安装,则直接通过AUR管理工具卸载,例如:`yay -Rs kd`
</pre></details>

## :gear: 用法和配置
Expand Down Expand Up @@ -168,8 +168,6 @@ theme = "temp"
http_proxy = ""

# 输出内容前自动清空终端,适合强迫症
clear_screen = false

# 是否开启频率提醒:本月第X次查询xxx
freq_alert = false

Expand All @@ -184,7 +182,6 @@ enable_emoji = true
path = ""
# 日志级别,支持:DEBUG/INFO/WARN/PANIC/FATAL
level = "WARN"
stderr = false
```

## 🎈 提升体验技巧
Expand All @@ -208,20 +205,9 @@ fi

### 通过systemd管理daemon进程

为避免每次开机后第一次查询都要等待守护进程启动,可以创建service文件`/usr/lib/systemd/user/kd-server.service`,然后执行`systemctl enable --user kd-server`,daemon进程将随系统自动启动:

```
[Unit]
Description=kd the command-line dictionary's server
[Service]
Type=simple
ExecStart=/usr/bin/kd --server
Restart=always
为避免每次开机后第一次查询都要等待守护进程启动,可以创建service文件`/usr/lib/systemd/user/kd-server.service`,然后执行`systemctl enable --user kd-server`,daemon进程将随系统自动启动

[Install]
WantedBy=default.target
```
内容参考[kd-server.service](./scripts/kd-server.service)

## 🎨 颜色主题

Expand Down Expand Up @@ -273,7 +259,7 @@ sudo xattr -r -d com.apple.quarantine <kd文件所在路径>
- 增加多种主题,包含常见配色如Gruvbox/Molokai,仿照bat实现
- 支持全模块自定义显示配置
- 引入多种查询源和词库,如stardict、bing等
- 增加服务端
- 增加远程服务端
- 支持通过fzf补全
- Vim插件,浮窗显示查词结果
- 离线词库周期更新
Expand Down
16 changes: 6 additions & 10 deletions cmd/kd.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ import (
"go.uber.org/zap"
)

var VERSION = "v0.0.8"
var VERSION = "v0.0.9"

func showPrompt() {
exename, err := pkg.GetExecutableBasename()
if err != nil {
d.EchoFatal(err.Error())
}
fmt.Printf(`%[1]s <text> 查单词、词组
%[1]s -t <text> 查长句
%[1]s -h 查看详细帮助
`, exename)
}
Expand All @@ -49,6 +50,7 @@ var um = map[string]string{
"generate-config": "generate config sample 生成配置文件,Linux/Mac默认地址为~/.config/kd.toml,Win为~\\kd.toml",
"edit-config": "edit configuration file with the default editor 用默认编辑器打开配置文件",
"status": "show running status 展示运行信息",
"log-to-stream": "redirect logging output to stdout&stderr (for debugging or server mode)",
}

// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -86,16 +88,12 @@ func flagStop(*cli.Context, bool) error {
}

func flagRestart(*cli.Context, bool) error {
err := daemon.KillDaemonIfRunning()
if err == nil {
err = daemon.StartDaemonProcess()
}
return err
return daemon.RestartDaemon()
}

func flagUpdate(ctx *cli.Context, _ bool) (err error) {
var ver string
if pkg.GetLinuxDistro() == "arch" {
if runtime.GOOS == "linux" && pkg.GetLinuxDistro() == "arch" {
d.EchoFine("您在使用ArchLinux,推荐直接通过AUR安装/升级(例如`yay -S kd`),更便于维护")
}
force := ctx.Bool("force")
Expand Down Expand Up @@ -241,9 +239,6 @@ func main() {
}
defer cache.LiteDB.Close()
defer core.WG.Wait()
// emoji.Println(":beer: Beer!!!")
// pizzaMessage := emoji.Sprint("I like a :pizza: and :sushi:!!")
// fmt.Println(pizzaMessage)

app := &cli.App{
Suggest: true, // XXX
Expand Down Expand Up @@ -271,6 +266,7 @@ func main() {
&cli.BoolFlag{Name: "generate-config", DisableDefaultText: true, Action: flagGenerateConfig, Usage: um["generate-config"]},
&cli.BoolFlag{Name: "edit-config", DisableDefaultText: true, Action: flagEditConfig, Usage: um["edit-config"]},
&cli.BoolFlag{Name: "status", DisableDefaultText: true, Hidden: true, Action: flagStatus, Usage: um["status"]},
&cli.BoolFlag{Name: "log-to-stream", DisableDefaultText: true, Hidden: true, Action: flagStatus, Usage: um["log-to-stream"]},
},
Action: func(cCtx *cli.Context) error {
// 除了--text外,其他的BoolFlag都当subcommand用
Expand Down
17 changes: 12 additions & 5 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import (
var CONFIG_PATH string

type LoggerConfig struct {
Enable bool `default:"true" toml:"enable"`
Path string `toml:"path"`
Level string `default:"warn" toml:"level"`
Stderr bool `default:"false" toml:"stderr"`
Enable bool `default:"true" toml:"enable"`
Path string `toml:"path"`
Level string `default:"warn" toml:"level"`
Stderr bool `default:"false" toml:"stderr"`
RedirectToStream bool `default:"false" toml:"redirect_to_stream"`
}

type Config struct {
Expand Down Expand Up @@ -58,9 +59,15 @@ func (c *Config) CheckAndApply() (err error) {
c.Logging.Level = "warn"
} else if !str.InSlice(c.Logging.Level, []string{"debug", "info", "warn", "panic", "fatal"}) {
return fmt.Errorf("[logging.level] 不支持的日志等级:%s", c.Logging.Level)

}
}
for _, arg := range os.Args {
if arg == "--log-to-stream" {
c.Logging.Enable = true
c.Logging.RedirectToStream = true
break
}
}
return
}

Expand Down
62 changes: 39 additions & 23 deletions internal/daemon/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,15 @@ import (
"github.com/Karmenzind/kd/pkg"
d "github.com/Karmenzind/kd/pkg/decorate"
"github.com/Karmenzind/kd/pkg/proc"
"github.com/Karmenzind/kd/pkg/systemd"

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

// type model.RunInfo struct {
// *proc.ProcInfo
// Port string
// Version string
// }

var SYSTEMD_UNIT_NAME = "kd-server"
var DaemonInfo = &model.RunInfo{}

// func RecordRunInfo(port string) {
// run.Info.Port = port

// err := pkg.SaveJson(filepath.Join(run.CACHE_RUN_PATH, "daemon.json"), run.Info)
// if err == nil {
// zap.S().Infof("Recorded running information of daemon %+v", DaemonInfo)
// } else {
// zap.S().Warnf("Failed to record running info of daemon %+v", err)
// }
// }

func GetDaemonInfoPath() string {
return filepath.Join(run.CACHE_RUN_PATH, "daemon.json")
}
Expand Down Expand Up @@ -103,12 +88,7 @@ func FindServerProcess() (*process.Process, error) {

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)
// zap.S().Debugf("Found process kd with CMD: %s", cmd)
if strings.Contains(cmd, " --server") {
zap.S().Debugf("Found process %+v Cmd: `%s`", p, cmd)
return p, nil
Expand Down Expand Up @@ -154,6 +134,16 @@ func StartDaemonProcess() error {
}

func KillDaemonIfRunning() error {
if runtime.GOOS == "linux" {
if yes, _ := systemd.ServiceIsActive(SYSTEMD_UNIT_NAME, true); yes {
d.EchoWarn("检测到daemon作为systemd unit运行,将使用systemctl停止,再次启动需执行systemctl start --user %s", SYSTEMD_UNIT_NAME)
_, err := systemd.StopService(SYSTEMD_UNIT_NAME, true)
if err == nil {
d.EchoOkay("已经通过systemd停止kd-server服务")
}
return err
}
}
p, err := FindServerProcess()
if err == nil {
if p == nil {
Expand All @@ -170,6 +160,32 @@ func KillDaemonIfRunning() error {
if err == nil {
zap.S().Info("Terminated daemon process.")
d.EchoOkay("守护进程已经停止")
} else {
zap.S().Warnf("Failed to terminate daemon process: %s", err)
}
return err
}

// TODO (k): <2024-05-05 15:56>
func SendHUP2Daemon() error {
return nil
}

func RestartDaemon() error {
if runtime.GOOS == "linux" {
if yes, _ := systemd.ServiceIsActive(SYSTEMD_UNIT_NAME, true); yes {
zap.S().Debugf("Found systemd unit: %s", SYSTEMD_UNIT_NAME)
d.EchoWarn("检测到daemon存在相应systemd unit,将使用systemctl重启")
_, err := systemd.RestartService(SYSTEMD_UNIT_NAME, true)
if err == nil {
d.EchoOkay("已经通过systemctl重启daemon服务")
}
return err
}
}
err := KillDaemonIfRunning()
if err == nil {
err = StartDaemonProcess()
}
return err
}
1 change: 1 addition & 0 deletions internal/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func StartServer() (err error) {
run.Info.SetPort(port)
go run.Info.SaveToFile(filepath.Join(run.CACHE_RUN_PATH, "daemon.json"))
d.EchoOkay("Listening on host: %s, port: %s\n", host, port)
zap.S().Info("Started kd server")

daemon.InitCron()

Expand Down
41 changes: 23 additions & 18 deletions logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,32 @@ var LOG_FILE string

func buildLogger(logCfg *config.LoggerConfig, options ...zap.Option) (*zap.Logger, error) {
cfg := zap.NewDevelopmentConfig()
var f string
if logCfg.Path == "" {
u, err := user.Current()
if err != nil {
f = filepath.Join(os.TempDir(), "kd.log")
if logCfg.RedirectToStream {
LOG_FILE = "[stream]"
cfg.OutputPaths = []string{"stdout"}
cfg.ErrorOutputPaths = []string{"stderr"}
} else {
var f string
if logCfg.Path == "" {
u, err := user.Current()
if err != nil {
f = filepath.Join(os.TempDir(), "kd.log")
} else {
name := strings.ReplaceAll(u.Username, " ", "_")
name = strings.ReplaceAll(name, "\\", "_")
f = filepath.Join(os.TempDir(), fmt.Sprintf("kd_%s.log", name))
}
} else {
name := strings.ReplaceAll(u.Username, " ", "_")
name = strings.ReplaceAll(name, "\\", "_")
f = filepath.Join(os.TempDir(), fmt.Sprintf("kd_%s.log", name))
f = logCfg.Path
}
} else {
f = logCfg.Path
}
if _, err := os.Stat(f); err == nil {
os.Chmod(f, 0o666)
}
LOG_FILE = f

cfg.OutputPaths = []string{f}
cfg.ErrorOutputPaths = []string{f}
if _, err := os.Stat(f); err == nil {
os.Chmod(f, 0o666)
}
LOG_FILE = f

cfg.OutputPaths = []string{f}
cfg.ErrorOutputPaths = []string{f}
}
level, err := zap.ParseAtomicLevel(logCfg.Level)
if err != nil {
return nil, err
Expand Down
9 changes: 8 additions & 1 deletion pkg/proc/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ 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 {
yes, errCheck := p.IsRunning()
zap.S().Infof("Checking if running (pid %v): %v err: %v", p.Pid, yes, errCheck)
if errCheck == nil && !yes {
zap.S().Infof("Stopped process %v with SIGINT.", p.Pid)
return
}
Expand Down Expand Up @@ -49,3 +51,8 @@ func SysKillPID(pid int32) (err error) {
}
return
}

func SendSignalToProcess(pid int32, signal syscall.Signal) error {
return nil
}

Loading

0 comments on commit 500bafb

Please sign in to comment.