mirror of
https://github.com/teest114514/chatlog_alpha.git
synced 2026-03-22 10:48:23 +08:00
feat: add application mutex to prevent multiple instances
This commit is contained in:
@@ -36,6 +36,8 @@ var serverCmd = &cobra.Command{
|
||||
Use: "server",
|
||||
Short: "Start HTTP server",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cleanup := initSingleInstance()
|
||||
defer cleanup()
|
||||
|
||||
cmdConf := getServerConfig()
|
||||
log.Info().Msgf("server cmd config: %+v", cmdConf)
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package chatlog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/sjzar/chatlog/internal/chatlog"
|
||||
"github.com/sjzar/chatlog/pkg/process"
|
||||
"github.com/sjzar/chatlog/pkg/util"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -35,8 +40,20 @@ var rootCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func Root(cmd *cobra.Command, args []string) {
|
||||
cleanup := initSingleInstance()
|
||||
defer cleanup()
|
||||
|
||||
m := chatlog.New()
|
||||
if err := m.Run(""); err != nil {
|
||||
log.Err(err).Msg("failed to run chatlog instance")
|
||||
}
|
||||
}
|
||||
|
||||
func initSingleInstance() func() {
|
||||
cleanup, err := process.CheckSingleInstance(util.DefaultWorkDir(""))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return cleanup
|
||||
}
|
||||
|
||||
65
pkg/process/process.go
Normal file
65
pkg/process/process.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/process"
|
||||
)
|
||||
|
||||
// CheckSingleInstance checks if another instance is running using a PID file.
|
||||
// If another instance is found, it prompts the user to force close it.
|
||||
// Returns a cleanup function to be called on exit.
|
||||
func CheckSingleInstance(workDir string) (func(), error) {
|
||||
pidFile := filepath.Join(workDir, "chatlog.pid")
|
||||
|
||||
// Read existing PID file
|
||||
if content, err := os.ReadFile(pidFile); err == nil {
|
||||
pidStr := strings.TrimSpace(string(content))
|
||||
if pid, err := strconv.Atoi(pidStr); err == nil {
|
||||
// Check if process exists
|
||||
if exists, _ := process.PidExists(int32(pid)); exists {
|
||||
// Process exists, check if it's really us (optional, but good practice)
|
||||
// For now, just assume if PID exists it might be us or a zombie.
|
||||
// We can check process name if needed, but pid file is strong hint.
|
||||
|
||||
fmt.Printf("Detected another instance running (PID: %d).\n", pid)
|
||||
fmt.Print("Do you want to force close it and continue? [y/N]: ")
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
input, _ := reader.ReadString('\n')
|
||||
input = strings.TrimSpace(strings.ToLower(input))
|
||||
|
||||
if input == "y" || input == "yes" {
|
||||
if p, err := process.NewProcess(int32(pid)); err == nil {
|
||||
if err := p.Kill(); err != nil {
|
||||
return nil, fmt.Errorf("failed to kill process: %w", err)
|
||||
}
|
||||
fmt.Println("Process killed.")
|
||||
} else {
|
||||
// Process might have exited in the meantime
|
||||
fmt.Println("Process not found, continuing...")
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("application already running")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write current PID
|
||||
currentPID := os.Getpid()
|
||||
if err := os.WriteFile(pidFile, []byte(strconv.Itoa(currentPID)), 0644); err != nil {
|
||||
return nil, fmt.Errorf("failed to write pid file: %w", err)
|
||||
}
|
||||
|
||||
// Cleanup function
|
||||
return func() {
|
||||
os.Remove(pidFile)
|
||||
},
|
||||
nil
|
||||
}
|
||||
Reference in New Issue
Block a user