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

141 lines
3.4 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/httpclient"
"github.com/curusarn/resh/internal/logger"
"go.uber.org/zap"
)
// info passed during build
var version string
var commit string
var developement bool
func main() {
config, errCfg := cfg.New()
logger, err := logger.New("daemon", config.LogLevel, developement)
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("Deamon starting ...",
"version", version,
"commit", commit,
)
// TODO: rethink PID file and logs location
homeDir, err := os.UserHomeDir()
if err != nil {
sugar.Fatalw("Could not get user home dir", zap.Error(err))
}
PIDFile := filepath.Join(homeDir, ".resh/resh.pid")
reshHistoryPath := filepath.Join(homeDir, ".resh_history.json")
bashHistoryPath := filepath.Join(homeDir, ".bash_history")
zshHistoryPath := filepath.Join(homeDir, ".zsh_history")
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.Warn("Pidfile exists")
// 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 pidfile",
"error", err,
"PIDFile", PIDFile,
)
}
server := Server{
sugar: sugar,
config: config,
reshHistoryPath: reshHistoryPath,
bashHistoryPath: bashHistoryPath,
zshHistoryPath: zshHistoryPath,
}
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) getEnvOrPanic(envVar string) string {
val, found := os.LookupEnv(envVar)
if !found {
d.sugar.Fatalw("Required env variable is not set",
"variableName", envVar,
)
}
return val
}
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
}