Rich Enhanced Shell History - Contextual shell history for zsh and bash
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.
 
 
 
resh/cmd/daemon/main.go

148 lines
3.7 KiB

package main
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"github.com/curusarn/resh/internal/cfg"
"github.com/curusarn/resh/internal/datadir"
"github.com/curusarn/resh/internal/device"
"github.com/curusarn/resh/internal/httpclient"
"github.com/curusarn/resh/internal/logger"
"go.uber.org/zap"
)
// info passed during build
var version string
var commit string
var development string
func main() {
config, errCfg := cfg.New()
logger, err := logger.New("daemon", config.LogLevel, development)
if err != nil {
fmt.Printf("Error while creating logger: %v", err)
}
defer logger.Sync() // flushes buffer, if any
if errCfg != nil {
logger.Error("Error while getting configuration", zap.Error(errCfg))
}
sugar := logger.Sugar()
d := daemon{sugar: sugar}
sugar.Infow("Daemon starting ...",
"version", version,
"commit", commit,
)
dataDir, err := datadir.MakePath()
if err != nil {
sugar.Fatalw("Could not get user data directory", zap.Error(err))
}
homeDir, err := os.UserHomeDir()
if err != nil {
sugar.Fatalw("Could not get user home directory", zap.Error(err))
}
// TODO: These paths should be probably defined in a package
pidFile := filepath.Join(dataDir, "daemon.pid")
reshHistoryPath := filepath.Join(dataDir, "history.reshjson")
bashHistoryPath := filepath.Join(homeDir, ".bash_history")
zshHistoryPath := filepath.Join(homeDir, ".zsh_history")
deviceID, err := device.GetID(dataDir)
if err != nil {
sugar.Fatalw("Could not get resh device ID", zap.Error(err))
}
deviceName, err := device.GetName(dataDir)
if err != nil {
sugar.Fatalw("Could not get resh device name", zap.Error(err))
}
sugar = sugar.With(zap.Int("daemonPID", os.Getpid()))
res, err := d.isDaemonRunning(config.Port)
if err != nil {
sugar.Errorw("Error while checking daemon status - it's probably not running",
"error", err)
}
if res {
sugar.Errorw("Daemon is already running - exiting!")
return
}
_, err = os.Stat(pidFile)
if err == nil {
sugar.Warnw("PID file exists",
"PIDFile", pidFile)
// kill daemon
err = d.killDaemon(pidFile)
if err != nil {
sugar.Errorw("Could not kill daemon",
"error", err,
)
}
}
err = ioutil.WriteFile(pidFile, []byte(strconv.Itoa(os.Getpid())), 0644)
if err != nil {
sugar.Fatalw("Could not create PID file",
"error", err,
"PIDFile", pidFile,
)
}
server := Server{
sugar: sugar,
config: config,
reshHistoryPath: reshHistoryPath,
bashHistoryPath: bashHistoryPath,
zshHistoryPath: zshHistoryPath,
deviceID: deviceID,
deviceName: deviceName,
}
server.Run()
sugar.Infow("Removing PID file ...",
"PIDFile", pidFile,
)
err = os.Remove(pidFile)
if err != nil {
sugar.Errorw("Could not delete PID file", "error", err)
}
sugar.Info("Shutting down ...")
}
type daemon struct {
sugar *zap.SugaredLogger
}
func (d *daemon) isDaemonRunning(port int) (bool, error) {
url := "http://localhost:" + strconv.Itoa(port) + "/status"
client := httpclient.New()
resp, err := client.Get(url)
if err != nil {
return false, err
}
defer resp.Body.Close()
return true, nil
}
func (d *daemon) killDaemon(pidFile string) error {
dat, err := ioutil.ReadFile(pidFile)
if err != nil {
d.sugar.Errorw("Reading PID file failed",
"PIDFile", pidFile,
"error", err)
}
d.sugar.Infow("Successfully read PID file", "contents", string(dat))
pid, err := strconv.Atoi(strings.TrimSuffix(string(dat), "\n"))
if err != nil {
return fmt.Errorf("could not parse PID file contents: %w", err)
}
d.sugar.Infow("Successfully parsed PID", "PID", pid)
cmd := exec.Command("kill", "-s", "sigint", strconv.Itoa(pid))
err = cmd.Run()
if err != nil {
return fmt.Errorf("kill command finished with error: %w", err)
}
return nil
}