From e12e366dda6be0d84538b3e538d49094e9b3c245 Mon Sep 17 00:00:00 2001 From: Simon Let Date: Sun, 6 Oct 2019 19:05:02 +0200 Subject: [PATCH] draft of arrow bindings add session history dispatch extend reshctl add recall - both command and handler for the daemon add session_init --- Makefile | 12 +- cmd/collect/main.go | 17 ++- cmd/control/cmd/debug.go | 23 +++- cmd/control/cmd/root.go | 2 + cmd/control/status/status.go | 2 + cmd/daemon/main.go | 82 +------------- cmd/daemon/recall.go | 48 ++++++++ cmd/daemon/record.go | 47 ++++++++ cmd/daemon/run-server.go | 46 ++++++++ cmd/daemon/session-init.go | 38 +++++++ cmd/postcollect/main.go | 2 +- cmd/session-init/main.go | 186 ++++++++++++++++++++++++++++++ pkg/collect/collect.go | 44 +++++++- pkg/records/records.go | 4 +- pkg/sess/sess.go | 7 ++ pkg/sesshist/sesshist.go | 135 ++++++++++++++++++++++ pkg/sesswatch/sesswatch.go | 21 ++-- scripts/bindutil.sh | 37 ------ scripts/hooks.sh | 147 ++++++++++++++++++++++++ scripts/reshctl.sh | 43 ++++++- scripts/shellrc.sh | 212 ++--------------------------------- scripts/util.sh | 136 ++++++++++++++++++++++ scripts/widgets.sh | 26 ++++- 23 files changed, 964 insertions(+), 353 deletions(-) create mode 100644 cmd/daemon/recall.go create mode 100644 cmd/daemon/record.go create mode 100644 cmd/daemon/run-server.go create mode 100644 cmd/daemon/session-init.go create mode 100644 cmd/session-init/main.go create mode 100644 pkg/sess/sess.go create mode 100644 pkg/sesshist/sesshist.go delete mode 100644 scripts/bindutil.sh create mode 100644 scripts/hooks.sh create mode 100644 scripts/util.sh diff --git a/Makefile b/Makefile index 277de70..9b86acd 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ sanitize: # # -build: submodules bin/resh-collect bin/resh-postcollect bin/resh-daemon bin/resh-evaluate bin/resh-sanitize bin/resh-control +build: submodules bin/resh-session-init bin/resh-collect bin/resh-postcollect bin/resh-daemon bin/resh-evaluate bin/resh-sanitize bin/resh-control test_go: # Running tests @@ -69,7 +69,7 @@ install: build submodules/bash-preexec/bash-preexec.sh scripts/shellrc.sh conf/c cp -f conf/config.toml ~/.config/resh.toml cp -f scripts/shellrc.sh ~/.resh/shellrc - cp -f scripts/reshctl.sh scripts/bindutil.sh scripts/widgets.sh ~/.resh/ + cp -f scripts/reshctl.sh scripts/widgets.sh scripts/hooks.sh scripts/util.sh ~/.resh/ bin/resh-control completion bash > ~/.resh/bash_completion.d/_reshctl bin/resh-control completion zsh > ~/.resh/zsh_completion.d/_reshctl @@ -92,6 +92,12 @@ install: build submodules/bash-preexec/bash-preexec.sh scripts/shellrc.sh conf/c # Restarting resh daemon ... -[ ! -f ~/.resh/resh.pid ] || kill -SIGTERM $$(cat ~/.resh/resh.pid) nohup resh-daemon &>/dev/null & disown + # Reloading rc files + . ~/.resh/shellrc + # Generating resh-uuid + [ -e "$(HOME)/.resh/resh-uuid" ] \ + || cat /proc/sys/kernel/random/uuid > "$(HOME)/.resh/resh-uuid" 2>/dev/null \ + || ./uuid.sh > "$(HOME)/.resh/resh-uuid" 2>/dev/null # Final touch touch ~/.resh_history.json # @@ -127,7 +133,7 @@ uninstall: bin/resh-control: cmd/control/cmd/*.go -bin/resh-%: cmd/%/main.go pkg/*/*.go VERSION +bin/resh-%: cmd/%/*.go pkg/*/*.go VERSION go build ${GOFLAGS} -o $@ cmd/$*/*.go $(HOME)/.resh $(HOME)/.resh/bin $(HOME)/.config $(HOME)/.resh/bash_completion.d $(HOME)/.resh/zsh_completion.d: diff --git a/cmd/collect/main.go b/cmd/collect/main.go index d2045c8..64ff6c8 100644 --- a/cmd/collect/main.go +++ b/cmd/collect/main.go @@ -35,6 +35,9 @@ func main() { if _, err := toml.DecodeFile(configPath, &config); err != nil { log.Fatal("Error reading config:", err) } + recall := flag.Bool("recall", false, "Recall command on position --histno") + recallHistno := flag.Int("histno", 0, "Recall command on position --histno") + showVersion := flag.Bool("version", false, "Show version and exit") showRevision := flag.Bool("revision", false, "Show git revision and exit") @@ -114,6 +117,10 @@ func main() { ")") os.Exit(3) } + if *recallHistno != 0 && *recall == false { + log.Println("Option '--recall' only works with '--histno' option - exiting!") + os.Exit(4) + } realtimeBefore, err := strconv.ParseFloat(*rtb, 64) if err != nil { log.Fatal("Flag Parsing error (rtb):", err) @@ -159,6 +166,8 @@ func main() { Lines: *lines, // core BaseRecord: records.BaseRecord{ + RecallHistno: *recallHistno, + CmdLine: *cmdLine, ExitCode: *exitCode, Shell: *shell, @@ -178,7 +187,7 @@ func main() { // non-posix RealPwd: realPwd, Pid: *pid, - SessionPid: *sessionPid, + SessionPID: *sessionPid, Host: *host, Hosttype: *hosttype, Ostype: *ostype, @@ -212,5 +221,9 @@ func main() { ReshRevision: Revision, }, } - collect.SendRecord(rec, strconv.Itoa(config.Port)) + if *recall { + fmt.Print(collect.SendRecallRequest(rec, strconv.Itoa(config.Port))) + } else { + collect.SendRecord(rec, strconv.Itoa(config.Port), "/record") + } } diff --git a/cmd/control/cmd/debug.go b/cmd/control/cmd/debug.go index c4dd299..fc10550 100644 --- a/cmd/control/cmd/debug.go +++ b/cmd/control/cmd/debug.go @@ -10,16 +10,33 @@ import ( "github.com/spf13/cobra" ) -// completionCmd represents the completion command var debugCmd = &cobra.Command{ Use: "debug", - Short: "Shows logs and output from last runs of resh", - Long: "Shows logs and output from last runs of resh", + Short: "Debug utils for resh", + Long: "Reloads resh rc files. Shows logs and output from last runs of resh", +} + +var debugReloadCmd = &cobra.Command{ + Use: "reload", + Short: "Reload resh rc files", + Long: "Reload resh rc files", + Run: func(cmd *cobra.Command, args []string) { + exitCode = status.ReloadRcFiles + }, +} + +var debugOutputCmd = &cobra.Command{ + Use: "output", + Short: "Shows output from last runs of resh", + Long: "Shows output from last runs of resh", Run: func(cmd *cobra.Command, args []string) { files := []string{ "daemon_last_run_out.txt", "collect_last_run_out.txt", "postcollect_last_run_out.txt", + "session_init_last_run_out.txt", + "arrow_up_last_run_out.txt", + "arrow_down_last_run_out.txt", } usr, _ := user.Current() dir := usr.HomeDir diff --git a/cmd/control/cmd/root.go b/cmd/control/cmd/root.go index b2f57a0..a00d342 100644 --- a/cmd/control/cmd/root.go +++ b/cmd/control/cmd/root.go @@ -28,6 +28,8 @@ func Execute() status.Code { completionCmd.AddCommand(completionZshCmd) rootCmd.AddCommand(debugCmd) + debugCmd.AddCommand(debugReloadCmd) + debugCmd.AddCommand(debugOutputCmd) if err := rootCmd.Execute(); err != nil { fmt.Println(err) return status.Fail diff --git a/cmd/control/status/status.go b/cmd/control/status/status.go index 28eb3a7..8b9eeec 100644 --- a/cmd/control/status/status.go +++ b/cmd/control/status/status.go @@ -12,4 +12,6 @@ const ( EnableAll = 100 // DisableAll exit code - tells reshctl() wrapper to disable_all DisableAll = 110 + // ReloadRcFiles exit code - tells reshctl() wrapper to reload shellrc resh file + ReloadRcFiles = 200 ) diff --git a/cmd/daemon/main.go b/cmd/daemon/main.go index 7d150af..e5fc2b1 100644 --- a/cmd/daemon/main.go +++ b/cmd/daemon/main.go @@ -1,7 +1,7 @@ package main import ( - "encoding/json" + //"flag" "io/ioutil" "log" @@ -15,9 +15,6 @@ import ( "github.com/BurntSushi/toml" "github.com/curusarn/resh/pkg/cfg" - "github.com/curusarn/resh/pkg/histfile" - "github.com/curusarn/resh/pkg/records" - "github.com/curusarn/resh/pkg/sesswatch" ) // Version from git set during build @@ -85,62 +82,6 @@ func statusHandler(w http.ResponseWriter, r *http.Request) { log.Println("Status OK") } -type recordHandler struct { - subscribers []chan records.Record -} - -func (h *recordHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("OK\n")) - jsn, err := ioutil.ReadAll(r.Body) - // run rest of the handler as goroutine to prevent any hangups - go func() { - if err != nil { - log.Println("Error reading the body", err) - return - } - - record := records.Record{} - err = json.Unmarshal(jsn, &record) - if err != nil { - log.Println("Decoding error: ", err) - log.Println("Payload: ", jsn) - return - } - for _, sub := range h.subscribers { - sub <- record - } - part := "2" - if record.PartOne { - part = "1" - } - log.Println("Received:", record.CmdLine, " - part", part) - }() - - // fmt.Println("cmd:", r.CmdLine) - // fmt.Println("pwd:", r.Pwd) - // fmt.Println("git:", r.GitWorkTree) - // fmt.Println("exit_code:", r.ExitCode) -} - -func runServer(config cfg.Config, outputPath string) { - var recordSubscribers []chan records.Record - - histfileChan := make(chan records.Record) - recordSubscribers = append(recordSubscribers, histfileChan) - sessionsToDrop := make(chan string) - histfile.Go(histfileChan, outputPath, sessionsToDrop) - - sesswatchChan := make(chan records.Record) - recordSubscribers = append(recordSubscribers, sesswatchChan) - sesswatch.Go(sesswatchChan, []chan string{sessionsToDrop}, config.SesswatchPeriodSeconds) - - http.HandleFunc("/status", statusHandler) - http.Handle("/record", &recordHandler{subscribers: recordSubscribers}) - //http.Handle("/session_init", &sessionInitHandler{OutputPath: outputPath}) - //http.Handle("/recall", &recallHandler{OutputPath: outputPath}) - http.ListenAndServe(":"+strconv.Itoa(config.Port), nil) -} - func killDaemon(pidfile string) error { dat, err := ioutil.ReadFile(pidfile) if err != nil { @@ -170,25 +111,4 @@ func isDaemonRunning(port int) (bool, error) { } defer resp.Body.Close() return true, nil - //body, err := ioutil.ReadAll(resp.Body) - - // dat, err := ioutil.ReadFile(pidfile) - // if err != nil { - // log.Println("Reading pid file failed", err) - // return false, err - // } - // log.Print(string(dat)) - // pid, err := strconv.ParseInt(string(dat), 10, 64) - // if err != nil { - // log.Fatal(err) - // } - // process, err := os.FindProcess(int(pid)) - // if err != nil { - // log.Printf("Failed to find process: %s\n", err) - // return false, err - // } else { - // err := process.Signal(syscall.Signal(0)) - // log.Printf("process.Signal on pid %d returned: %v\n", pid, err) - // } - // return true, nil } diff --git a/cmd/daemon/recall.go b/cmd/daemon/recall.go new file mode 100644 index 0000000..d9ba682 --- /dev/null +++ b/cmd/daemon/recall.go @@ -0,0 +1,48 @@ +package main + +import ( + "encoding/json" + "io/ioutil" + "log" + "net/http" + + "github.com/curusarn/resh/pkg/collect" + "github.com/curusarn/resh/pkg/records" + "github.com/curusarn/resh/pkg/sesshist" +) + +type recallHandler struct { + sesshistDispatch *sesshist.Dispatch +} + +func (h *recallHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + jsn, err := ioutil.ReadAll(r.Body) + if err != nil { + log.Println("Error reading the body", err) + return + } + + rec := records.Record{} + err = json.Unmarshal(jsn, &rec) + if err != nil { + log.Println("Decoding error:", err) + log.Println("Payload:", jsn) + return + } + cmd, err := h.sesshistDispatch.Recall(rec.SessionID, rec.RecallHistno) + if err != nil { + log.Println("/recall - sess id:", rec.SessionID, " - histno:", rec.RecallHistno, " -> ERROR") + log.Println("Recall error:", err) + return + } + resp := collect.SingleResponse{cmd} + jsn, err = json.Marshal(&resp) + if err != nil { + log.Println("Encoding error:", err) + log.Println("Response:", resp) + return + } + log.Println(string(jsn)) + w.Write(jsn) + log.Println("/recall - sess id:", rec.SessionID, " - histno:", rec.RecallHistno, " -> ", cmd) +} diff --git a/cmd/daemon/record.go b/cmd/daemon/record.go new file mode 100644 index 0000000..ee403f8 --- /dev/null +++ b/cmd/daemon/record.go @@ -0,0 +1,47 @@ +package main + +import ( + "encoding/json" + "io/ioutil" + "log" + "net/http" + + "github.com/curusarn/resh/pkg/records" +) + +type recordHandler struct { + subscribers []chan records.Record +} + +func (h *recordHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("OK\n")) + jsn, err := ioutil.ReadAll(r.Body) + // run rest of the handler as goroutine to prevent any hangups + go func() { + if err != nil { + log.Println("Error reading the body", err) + return + } + + record := records.Record{} + err = json.Unmarshal(jsn, &record) + if err != nil { + log.Println("Decoding error: ", err) + log.Println("Payload: ", jsn) + return + } + for _, sub := range h.subscribers { + sub <- record + } + part := "2" + if record.PartOne { + part = "1" + } + log.Println("/record - ", record.CmdLine, " - part", part) + }() + + // fmt.Println("cmd:", r.CmdLine) + // fmt.Println("pwd:", r.Pwd) + // fmt.Println("git:", r.GitWorkTree) + // fmt.Println("exit_code:", r.ExitCode) +} diff --git a/cmd/daemon/run-server.go b/cmd/daemon/run-server.go new file mode 100644 index 0000000..92d725f --- /dev/null +++ b/cmd/daemon/run-server.go @@ -0,0 +1,46 @@ +package main + +import ( + "net/http" + "strconv" + + "github.com/curusarn/resh/pkg/cfg" + "github.com/curusarn/resh/pkg/histfile" + "github.com/curusarn/resh/pkg/records" + "github.com/curusarn/resh/pkg/sesshist" + "github.com/curusarn/resh/pkg/sesswatch" +) + +func runServer(config cfg.Config, outputPath string) { + var recordSubscribers []chan records.Record + var sessionInitSubscribers []chan records.Record + var sessionDropSubscribers []chan string + + // sessshist + sesshistSessionsToInit := make(chan records.Record) + sessionInitSubscribers = append(sessionInitSubscribers, sesshistSessionsToInit) + sesshistSessionsToDrop := make(chan string) + sessionDropSubscribers = append(sessionDropSubscribers, sesshistSessionsToDrop) + sesshistRecords := make(chan records.Record) + recordSubscribers = append(recordSubscribers, sesshistRecords) + sesshistDispatch := sesshist.NewDispatch(sesshistSessionsToInit, sesshistSessionsToDrop, sesshistRecords) + + // histfile + histfileRecords := make(chan records.Record) + recordSubscribers = append(recordSubscribers, histfileRecords) + histfileSessionsToDrop := make(chan string) + sessionDropSubscribers = append(sessionDropSubscribers, histfileSessionsToDrop) + histfile.Go(histfileRecords, outputPath, histfileSessionsToDrop) + + // sesswatch + sesswatchSessionsToWatch := make(chan records.Record) + sessionInitSubscribers = append(sessionInitSubscribers, sesswatchSessionsToWatch) + sesswatch.Go(sesswatchSessionsToWatch, sessionDropSubscribers, config.SesswatchPeriodSeconds) + + // handlers + http.HandleFunc("/status", statusHandler) + http.Handle("/record", &recordHandler{subscribers: recordSubscribers}) + http.Handle("/session_init", &sessionInitHandler{subscribers: sessionInitSubscribers}) + http.Handle("/recall", &recallHandler{sesshistDispatch: sesshistDispatch}) + http.ListenAndServe(":"+strconv.Itoa(config.Port), nil) +} diff --git a/cmd/daemon/session-init.go b/cmd/daemon/session-init.go new file mode 100644 index 0000000..27a1b27 --- /dev/null +++ b/cmd/daemon/session-init.go @@ -0,0 +1,38 @@ +package main + +import ( + "encoding/json" + "io/ioutil" + "log" + "net/http" + + "github.com/curusarn/resh/pkg/records" +) + +type sessionInitHandler struct { + subscribers []chan records.Record +} + +func (h *sessionInitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("OK\n")) + jsn, err := ioutil.ReadAll(r.Body) + // run rest of the handler as goroutine to prevent any hangups + go func() { + if err != nil { + log.Println("Error reading the body", err) + return + } + + record := records.Record{} + err = json.Unmarshal(jsn, &record) + if err != nil { + log.Println("Decoding error: ", err) + log.Println("Payload: ", jsn) + return + } + for _, sub := range h.subscribers { + sub <- record + } + log.Println("/session_init - id:", record.SessionID, " - pid:", record.SessionPID) + }() +} diff --git a/cmd/postcollect/main.go b/cmd/postcollect/main.go index fc02704..edca110 100644 --- a/cmd/postcollect/main.go +++ b/cmd/postcollect/main.go @@ -143,5 +143,5 @@ func main() { ReshRevision: Revision, }, } - collect.SendRecord(rec, strconv.Itoa(config.Port)) + collect.SendRecord(rec, strconv.Itoa(config.Port), "/record") } diff --git a/cmd/session-init/main.go b/cmd/session-init/main.go new file mode 100644 index 0000000..b7c4516 --- /dev/null +++ b/cmd/session-init/main.go @@ -0,0 +1,186 @@ +package main + +import ( + "flag" + "fmt" + "log" + "os" + + "github.com/BurntSushi/toml" + "github.com/curusarn/resh/pkg/cfg" + "github.com/curusarn/resh/pkg/collect" + "github.com/curusarn/resh/pkg/records" + + "os/user" + "path/filepath" + "strconv" +) + +// Version from git set during build +var Version string + +// Revision from git set during build +var Revision string + +func main() { + usr, _ := user.Current() + dir := usr.HomeDir + configPath := filepath.Join(dir, "/.config/resh.toml") + reshUUIDPath := filepath.Join(dir, "/.resh/resh-uuid") + + machineIDPath := "/etc/machine-id" + + var config cfg.Config + if _, err := toml.DecodeFile(configPath, &config); err != nil { + log.Fatal("Error reading config:", err) + } + showVersion := flag.Bool("version", false, "Show version and exit") + showRevision := flag.Bool("revision", false, "Show git revision and exit") + + requireVersion := flag.String("requireVersion", "", "abort if version doesn't match") + requireRevision := flag.String("requireRevision", "", "abort if revision doesn't match") + + shell := flag.String("shell", "", "actual shell") + uname := flag.String("uname", "", "uname") + sessionID := flag.String("sessionId", "", "resh generated session id") + + // posix variables + cols := flag.String("cols", "-1", "$COLUMNS") + lines := flag.String("lines", "-1", "$LINES") + home := flag.String("home", "", "$HOME") + lang := flag.String("lang", "", "$LANG") + lcAll := flag.String("lcAll", "", "$LC_ALL") + login := flag.String("login", "", "$LOGIN") + shellEnv := flag.String("shellEnv", "", "$SHELL") + term := flag.String("term", "", "$TERM") + + // non-posix + pid := flag.Int("pid", -1, "$$") + sessionPid := flag.Int("sessionPid", -1, "$$ at session start") + shlvl := flag.Int("shlvl", -1, "$SHLVL") + + host := flag.String("host", "", "$HOSTNAME") + hosttype := flag.String("hosttype", "", "$HOSTTYPE") + ostype := flag.String("ostype", "", "$OSTYPE") + machtype := flag.String("machtype", "", "$MACHTYPE") + + // before after + timezoneBefore := flag.String("timezoneBefore", "", "") + + osReleaseID := flag.String("osReleaseId", "", "/etc/os-release ID") + osReleaseVersionID := flag.String("osReleaseVersionId", "", + "/etc/os-release ID") + osReleaseIDLike := flag.String("osReleaseIdLike", "", "/etc/os-release ID") + osReleaseName := flag.String("osReleaseName", "", "/etc/os-release ID") + osReleasePrettyName := flag.String("osReleasePrettyName", "", + "/etc/os-release ID") + + rtb := flag.String("realtimeBefore", "-1", "before $EPOCHREALTIME") + rtsess := flag.String("realtimeSession", "-1", + "on session start $EPOCHREALTIME") + rtsessboot := flag.String("realtimeSessSinceBoot", "-1", + "on session start $EPOCHREALTIME") + flag.Parse() + + if *showVersion == true { + fmt.Println(Version) + os.Exit(0) + } + if *showRevision == true { + fmt.Println(Revision) + os.Exit(0) + } + if *requireVersion != "" && *requireVersion != Version { + fmt.Println("Please restart/reload this terminal session " + + "(resh version: " + Version + + "; resh version of this terminal session: " + *requireVersion + + ")") + os.Exit(3) + } + if *requireRevision != "" && *requireRevision != Revision { + fmt.Println("Please restart/reload this terminal session " + + "(resh revision: " + Revision + + "; resh revision of this terminal session: " + *requireRevision + + ")") + os.Exit(3) + } + realtimeBefore, err := strconv.ParseFloat(*rtb, 64) + if err != nil { + log.Fatal("Flag Parsing error (rtb):", err) + } + realtimeSessionStart, err := strconv.ParseFloat(*rtsess, 64) + if err != nil { + log.Fatal("Flag Parsing error (rt sess):", err) + } + realtimeSessSinceBoot, err := strconv.ParseFloat(*rtsessboot, 64) + if err != nil { + log.Fatal("Flag Parsing error (rt sess boot):", err) + } + realtimeSinceSessionStart := realtimeBefore - realtimeSessionStart + realtimeSinceBoot := realtimeSessSinceBoot + realtimeSinceSessionStart + + timezoneBeforeOffset := collect.GetTimezoneOffsetInSeconds(*timezoneBefore) + realtimeBeforeLocal := realtimeBefore + timezoneBeforeOffset + + if *osReleaseID == "" { + *osReleaseID = "linux" + } + if *osReleaseName == "" { + *osReleaseName = "Linux" + } + if *osReleasePrettyName == "" { + *osReleasePrettyName = "Linux" + } + + rec := records.Record{ + // posix + Cols: *cols, + Lines: *lines, + // core + BaseRecord: records.BaseRecord{ + Shell: *shell, + Uname: *uname, + SessionID: *sessionID, + + // posix + Home: *home, + Lang: *lang, + LcAll: *lcAll, + Login: *login, + // Path: *path, + ShellEnv: *shellEnv, + Term: *term, + + // non-posix + Pid: *pid, + SessionPID: *sessionPid, + Host: *host, + Hosttype: *hosttype, + Ostype: *ostype, + Machtype: *machtype, + Shlvl: *shlvl, + + // before after + TimezoneBefore: *timezoneBefore, + + RealtimeBefore: realtimeBefore, + RealtimeBeforeLocal: realtimeBeforeLocal, + + RealtimeSinceSessionStart: realtimeSinceSessionStart, + RealtimeSinceBoot: realtimeSinceBoot, + + MachineID: collect.ReadFileContent(machineIDPath), + + OsReleaseID: *osReleaseID, + OsReleaseVersionID: *osReleaseVersionID, + OsReleaseIDLike: *osReleaseIDLike, + OsReleaseName: *osReleaseName, + OsReleasePrettyName: *osReleasePrettyName, + + ReshUUID: collect.ReadFileContent(reshUUIDPath), + ReshVersion: Version, + ReshRevision: Revision, + }, + } + collect.SendRecord(rec, strconv.Itoa(config.Port), "/session_init") +} diff --git a/pkg/collect/collect.go b/pkg/collect/collect.go index e53ef02..7f26b4e 100644 --- a/pkg/collect/collect.go +++ b/pkg/collect/collect.go @@ -13,14 +13,54 @@ import ( "github.com/curusarn/resh/pkg/records" ) +// SingleResponse json struct +type SingleResponse struct { + CmdLine string `json:"cmdline"` +} + +// SendRecallRequest to daemon +func SendRecallRequest(r records.Record, port string) string { + recJSON, err := json.Marshal(r) + if err != nil { + log.Fatal("send err 1", err) + } + + req, err := http.NewRequest("POST", "http://localhost:"+port+"/recall", + bytes.NewBuffer(recJSON)) + if err != nil { + log.Fatal("send err 2", err) + } + req.Header.Set("Content-Type", "application/json") + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Fatal("resh-daemon is not running :(") + } + + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatal("read response error") + } + log.Println(string(body)) + response := SingleResponse{} + err = json.Unmarshal(body, &response) + if err != nil { + log.Fatal("unmarshal resp error: ", err) + } + log.Println(response) + return response.CmdLine +} + // SendRecord to daemon -func SendRecord(r records.Record, port string) { +func SendRecord(r records.Record, port, path string) { recJSON, err := json.Marshal(r) if err != nil { log.Fatal("send err 1", err) } - req, err := http.NewRequest("POST", "http://localhost:"+port+"/record", + req, err := http.NewRequest("POST", "http://localhost:"+port+path, bytes.NewBuffer(recJSON)) if err != nil { log.Fatal("send err 2", err) diff --git a/pkg/records/records.go b/pkg/records/records.go index 0ded51b..ce89a8b 100644 --- a/pkg/records/records.go +++ b/pkg/records/records.go @@ -35,7 +35,7 @@ type BaseRecord struct { RealPwd string `json:"realPwd"` RealPwdAfter string `json:"realPwdAfter"` Pid int `json:"pid"` - SessionPid int `json:"sessionPid"` + SessionPID int `json:"sessionPid"` Host string `json:"host"` Hosttype string `json:"hosttype"` Ostype string `json:"ostype"` @@ -82,7 +82,7 @@ type BaseRecord struct { // recall metadata Recalled bool `json:"recalled"` - RecallHistno string `json:"recallHistno,omitempty"` + RecallHistno int `json:"recallHistno,omitempty"` RecallStrategy string `json:"recallStrategy,omitempty"` RecallActions []string `json:"recallActions,omitempty"` diff --git a/pkg/sess/sess.go b/pkg/sess/sess.go new file mode 100644 index 0000000..f2e0fb8 --- /dev/null +++ b/pkg/sess/sess.go @@ -0,0 +1,7 @@ +package sess + +// Session represents a session, used for sennding through channels when more than just ID is needed +type Session struct { + ID string + PID int +} diff --git a/pkg/sesshist/sesshist.go b/pkg/sesshist/sesshist.go new file mode 100644 index 0000000..7a36492 --- /dev/null +++ b/pkg/sesshist/sesshist.go @@ -0,0 +1,135 @@ +package sesshist + +import ( + "errors" + "log" + "strconv" + "sync" + + "github.com/curusarn/resh/pkg/records" +) + +// Dispatch Recall() calls to an apropriate session history (sesshist) +type Dispatch struct { + sessions map[string]*sesshist + mutex sync.RWMutex +} + +// NewDispatch creates a new sesshist.Dispatch and starts necessary gorutines +func NewDispatch(sessionsToInit chan records.Record, sessionsToDrop chan string, recordsToAdd chan records.Record) *Dispatch { + s := Dispatch{sessions: map[string]*sesshist{}} + go s.sessionInitializer(sessionsToInit) + go s.sessionDropper(sessionsToDrop) + go s.recordAdder(recordsToAdd) + return &s +} + +func (s *Dispatch) sessionInitializer(sessionsToInit chan records.Record) { + for { + record := <-sessionsToInit + log.Println("sesshist: got session to init - " + record.SessionID) + s.initSession(record.SessionID) + } +} + +func (s *Dispatch) sessionDropper(sessionsToDrop chan string) { + for { + sessionID := <-sessionsToDrop + log.Println("sesshist: got session to drop - " + sessionID) + s.dropSession(sessionID) + } +} + +func (s *Dispatch) recordAdder(recordsToAdd chan records.Record) { + for { + record := <-recordsToAdd + if record.PartOne { + log.Println("sesshist: got record to add - " + record.CmdLine) + s.addRecentRecord(record.SessionID, record) + } + // TODO: we will need to handle part2 as well eventually + } +} + +// InitSession struct +func (s *Dispatch) initSession(sessionID string) error { + s.mutex.RLock() + _, found := s.sessions[sessionID] + s.mutex.RUnlock() + + if found == true { + return errors.New("sesshist ERROR: Can't INIT already existing session " + sessionID) + } + + s.mutex.Lock() + defer s.mutex.Unlock() + s.sessions[sessionID] = &sesshist{} + return nil +} + +// DropSession struct +func (s *Dispatch) dropSession(sessionID string) error { + s.mutex.RLock() + _, found := s.sessions[sessionID] + s.mutex.RUnlock() + + if found == false { + return errors.New("sesshist ERROR: Can't DROP not existing session " + sessionID) + } + + s.mutex.Lock() + defer s.mutex.Unlock() + delete(s.sessions, sessionID) + return nil +} + +// AddRecent record to session +func (s *Dispatch) addRecentRecord(sessionID string, record records.Record) error { + s.mutex.RLock() + session, found := s.sessions[sessionID] + s.mutex.RUnlock() + + if found == false { + return errors.New("sesshist ERROR: No session history for SessionID " + sessionID + " (should we create one?)") + } + session.mutex.Lock() + defer session.mutex.Unlock() + session.recent = append(session.recent, record) + log.Println("sesshist: record:", record.CmdLine, "; added to session:", sessionID, "; session len:", len(session.recent)) + return nil +} + +// Recall command from recent session history +func (s *Dispatch) Recall(sessionID string, histno int) (string, error) { + s.mutex.RLock() + session, found := s.sessions[sessionID] + s.mutex.RUnlock() + + if found == false { + return "", errors.New("sesshist ERROR: No session history for SessionID " + sessionID) + } + session.mutex.Lock() + defer session.mutex.Unlock() + return session.getRecordByHistno(histno) +} + +type sesshist struct { + recent []records.Record + mutex sync.Mutex +} + +func (s *sesshist) getRecordByHistno(histno int) (string, error) { + // records get appended to the end of the slice + // -> this func handles the indexing + if histno == 0 { + return "", errors.New("sesshist ERROR: 'histno == 0' is not a record from history") + } + if histno < 0 { + return "", errors.New("sesshist ERROR: 'histno < 0' is a command from future (not supperted yet)") + } + index := len(s.recent) - histno + if index < 0 { + return "", errors.New("sesshist ERROR: 'histno > number of commands in the session' (" + strconv.Itoa(len(s.recent)) + ")") + } + return s.recent[index].CmdLine, nil +} diff --git a/pkg/sesswatch/sesswatch.go b/pkg/sesswatch/sesswatch.go index 54b2c09..cb3d41e 100644 --- a/pkg/sesswatch/sesswatch.go +++ b/pkg/sesswatch/sesswatch.go @@ -18,28 +18,23 @@ type sesswatch struct { } // Go runs the session watcher - watches sessions and sends -func Go(input chan records.Record, sessionsToDrop []chan string, sleepSeconds uint) { +func Go(sessionsToWatch chan records.Record, sessionsToDrop []chan string, sleepSeconds uint) { sw := sesswatch{sessionsToDrop: sessionsToDrop, sleepSeconds: sleepSeconds, watchedSessions: map[string]bool{}} - go sw.waiter(input) + go sw.waiter(sessionsToWatch) } func (s *sesswatch) waiter(sessionsToWatch chan records.Record) { for { func() { record := <-sessionsToWatch - session := record.SessionID - pid := record.SessionPid - if record.PartOne == false { - log.Println("sesswatch: part2 - ignoring:", session, "~", pid) - return // continue - } - log.Println("sesswatch: got session ~ pid:", session, "~", pid) + id := record.SessionID + pid := record.SessionPID s.mutex.Lock() defer s.mutex.Unlock() - if s.watchedSessions[session] == false { - log.Println("sesswatch: start watching NEW session ~ pid:", session, "~", pid) - s.watchedSessions[session] = true - go s.watcher(session, record.SessionPid) + if s.watchedSessions[id] == false { + log.Println("sesswatch: start watching NEW session ~ pid:", id, "~", pid) + s.watchedSessions[id] = true + go s.watcher(id, pid) } }() } diff --git a/scripts/bindutil.sh b/scripts/bindutil.sh deleted file mode 100644 index 53e192e..0000000 --- a/scripts/bindutil.sh +++ /dev/null @@ -1,37 +0,0 @@ - - -# shellcheck source=../submodules/bash-zsh-compat-widgets/bindfunc.sh -. ~/.resh/bindfunc.sh -# shellcheck source=widgets.sh -. ~/.resh/widgets.sh - -__resh_bind_arrows() { - echo "bindfunc __resh_widget_arrow_up" - echo "bindfunc __resh_widget_arrow_down" - return 0 -} - -__resh_bind_control_R() { - echo "bindfunc __resh_widget_control_R" - return 0 -} -__resh_unbind_arrows() { - echo "\ bindfunc __resh_widget_arrow_up" - echo "\ bindfunc __resh_widget_arrow_down" - return 0 -} - -__resh_unbind_control_R() { - echo "\ bindfunc __resh_widget_control_R" - return 0 -} - -__resh_bind_all() { - __resh_bind_arrows - __resh_bind_control_R -} - -__resh_unbind_all() { - __resh_unbind_arrows - __resh_unbind_control_R -} diff --git a/scripts/hooks.sh b/scripts/hooks.sh new file mode 100644 index 0000000..322b5fc --- /dev/null +++ b/scripts/hooks.sh @@ -0,0 +1,147 @@ + +__resh_preexec() { + __RESH_HISTNO=0 + # core + __RESH_COLLECT=1 + __RESH_CMDLINE="$1" # not local to preserve it for postcollect (useful as sanity check) + __resh_collect --cmdLine "$__RESH_CMDLINE" \ + &>~/.resh/collect_last_run_out.txt || echo "resh-collect ERROR: $(head -n 1 ~/.resh/collect_last_run_out.txt)" +} + +# used for collect and collect --recall +__resh_collect() { + # posix + local __RESH_COLS="$COLUMNS" + local __RESH_LANG="$LANG" + local __RESH_LC_ALL="$LC_ALL" + # other LC ? + local __RESH_LINES="$LINES" + # __RESH_PATH="$PATH" + local __RESH_PWD="$PWD" + + # non-posix + local __RESH_SHLVL="$SHLVL" + local __RESH_GIT_CDUP; __RESH_GIT_CDUP="$(git rev-parse --show-cdup 2>/dev/null)" + local __RESH_GIT_CDUP_EXIT_CODE=$? + local __RESH_GIT_REMOTE; __RESH_GIT_REMOTE="$(git remote get-url origin 2>/dev/null)" + local __RESH_GIT_REMOTE_EXIT_CODE=$? + #__RESH_GIT_TOPLEVEL="$(git rev-parse --show-toplevel)" + #__RESH_GIT_TOPLEVEL_EXIT_CODE=$? + + if [ -n "$ZSH_VERSION" ]; then + # assume Zsh + local __RESH_PID="$$" # current pid + elif [ -n "$BASH_VERSION" ]; then + # assume Bash + local __RESH_PID="$BASHPID" # current pid + fi + # time + local __RESH_TZ_BEFORE; __RESH_TZ_BEFORE=$(date +%z) + # __RESH_RT_BEFORE="$EPOCHREALTIME" + __RESH_RT_BEFORE=$(__resh_get_epochrealtime) + + if [ "$__RESH_VERSION" != "$(resh-collect -version)" ]; then + # shellcheck source=shellrc.sh + source ~/.resh/shellrc + if [ "$__RESH_VERSION" != "$(resh-collect -version)" ]; then + echo "RESH WARNING: You probably just updated RESH - PLEASE RESTART OR RELOAD THIS TERMINAL SESSION (resh version: $(resh-collect -version); resh version of this terminal session: ${__RESH_VERSION})" + else + echo "RESH INFO: New RESH shellrc script was loaded - if you encounter any issues please restart this terminal session." + fi + elif [ "$__RESH_REVISION" != "$(resh-collect -revision)" ]; then + # shellcheck source=shellrc.sh + source ~/.resh/shellrc + if [ "$__RESH_REVISION" != "$(resh-collect -revision)" ]; then + echo "RESH WARNING: You probably just updated RESH - PLEASE RESTART OR RELOAD THIS TERMINAL SESSION (resh revision: $(resh-collect -revision); resh revision of this terminal session: ${__RESH_REVISION})" + fi + fi + if [ "$__RESH_VERSION" = "$(resh-collect -version)" ] && [ "$__RESH_REVISION" = "$(resh-collect -revision)" ]; then + resh-collect -requireVersion "$__RESH_VERSION" \ + -requireRevision "$__RESH_REVISION" \ + -shell "$__RESH_SHELL" \ + -uname "$__RESH_UNAME" \ + -sessionId "$__RESH_SESSION_ID" \ + -cols "$__RESH_COLS" \ + -home "$__RESH_HOME" \ + -lang "$__RESH_LANG" \ + -lcAll "$__RESH_LC_ALL" \ + -lines "$__RESH_LINES" \ + -login "$__RESH_LOGIN" \ + -pwd "$__RESH_PWD" \ + -shellEnv "$__RESH_SHELL_ENV" \ + -term "$__RESH_TERM" \ + -pid "$__RESH_PID" \ + -sessionPid "$__RESH_SESSION_PID" \ + -host "$__RESH_HOST" \ + -hosttype "$__RESH_HOSTTYPE" \ + -ostype "$__RESH_OSTYPE" \ + -machtype "$__RESH_MACHTYPE" \ + -shlvl "$__RESH_SHLVL" \ + -gitCdup "$__RESH_GIT_CDUP" \ + -gitCdupExitCode "$__RESH_GIT_CDUP_EXIT_CODE" \ + -gitRemote "$__RESH_GIT_REMOTE" \ + -gitRemoteExitCode "$__RESH_GIT_REMOTE_EXIT_CODE" \ + -realtimeBefore "$__RESH_RT_BEFORE" \ + -realtimeSession "$__RESH_RT_SESSION" \ + -realtimeSessSinceBoot "$__RESH_RT_SESS_SINCE_BOOT" \ + -timezoneBefore "$__RESH_TZ_BEFORE" \ + -osReleaseId "$__RESH_OS_RELEASE_ID" \ + -osReleaseVersionId "$__RESH_OS_RELEASE_VERSION_ID" \ + -osReleaseIdLike "$__RESH_OS_RELEASE_ID_LIKE" \ + -osReleaseName "$__RESH_OS_RELEASE_NAME" \ + -osReleasePrettyName "$__RESH_OS_RELEASE_PRETTY_NAME" \ + "$@" + fi +} + +__resh_precmd() { + local __RESH_EXIT_CODE=$? + local __RESH_RT_AFTER + local __RESH_TZ_AFTER + local __RESH_PWD_AFTER + local __RESH_GIT_CDUP_AFTER + local __RESH_GIT_CDUP_EXIT_CODE_AFTER + local __RESH_GIT_REMOTE_AFTER + local __RESH_GIT_REMOTE_EXIT_CODE_AFTER + __RESH_RT_AFTER=$(__resh_get_epochrealtime) + __RESH_TZ_AFTER=$(date +%z) + __RESH_PWD_AFTER="$PWD" + __RESH_GIT_CDUP_AFTER="$(git rev-parse --show-cdup 2>/dev/null)" + __RESH_GIT_CDUP_EXIT_CODE_AFTER=$? + __RESH_GIT_REMOTE_AFTER="$(git remote get-url origin 2>/dev/null)" + __RESH_GIT_REMOTE_EXIT_CODE_AFTER=$? + if [ -n "${__RESH_COLLECT}" ]; then + if [ "$__RESH_VERSION" != "$(resh-postcollect -version)" ]; then + # shellcheck source=shellrc.sh + source ~/.resh/shellrc + if [ "$__RESH_VERSION" != "$(resh-postcollect -version)" ]; then + echo "RESH WARNING: You probably just updated RESH - PLEASE RESTART OR RELOAD THIS TERMINAL SESSION (resh version: $(resh-collect -version); resh version of this terminal session: ${__RESH_VERSION})" + else + echo "RESH INFO: New RESH shellrc script was loaded - if you encounter any issues please restart this terminal session." + fi + elif [ "$__RESH_REVISION" != "$(resh-postcollect -revision)" ]; then + # shellcheck source=shellrc.sh + source ~/.resh/shellrc + if [ "$__RESH_REVISION" != "$(resh-postcollect -revision)" ]; then + echo "RESH WARNING: You probably just updated RESH - PLEASE RESTART OR RELOAD THIS TERMINAL SESSION (resh revision: $(resh-collect -revision); resh revision of this terminal session: ${__RESH_REVISION})" + fi + fi + if [ "$__RESH_VERSION" = "$(resh-postcollect -version)" ] && [ "$__RESH_REVISION" = "$(resh-postcollect -revision)" ]; then + resh-postcollect -requireVersion "$__RESH_VERSION" \ + -requireRevision "$__RESH_REVISION" \ + -cmdLine "$__RESH_CMDLINE" \ + -realtimeBefore "$__RESH_RT_BEFORE" \ + -exitCode "$__RESH_EXIT_CODE" \ + -sessionId "$__RESH_SESSION_ID" \ + -pwdAfter "$__RESH_PWD_AFTER" \ + -gitCdupAfter "$__RESH_GIT_CDUP_AFTER" \ + -gitCdupExitCodeAfter "$__RESH_GIT_CDUP_EXIT_CODE_AFTER" \ + -gitRemoteAfter "$__RESH_GIT_REMOTE_AFTER" \ + -gitRemoteExitCodeAfter "$__RESH_GIT_REMOTE_EXIT_CODE_AFTER" \ + -realtimeAfter "$__RESH_RT_AFTER" \ + -timezoneAfter "$__RESH_TZ_AFTER" \ + &>~/.resh/postcollect_last_run_out.txt || echo "resh-postcollect ERROR: $(head -n 1 ~/.resh/postcollect_last_run_out.txt)" + fi + fi + unset __RESH_COLLECT +} diff --git a/scripts/reshctl.sh b/scripts/reshctl.sh index 4676c43..5702153 100644 --- a/scripts/reshctl.sh +++ b/scripts/reshctl.sh @@ -1,7 +1,39 @@ -# source bindutil - contains functions to bind and unbind RESH widgets -# shellcheck source=bindutil.sh -. ~/.resh/bindutil.sh +# shellcheck source=../submodules/bash-zsh-compat-widgets/bindfunc.sh +. ~/.resh/bindfunc.sh +# shellcheck source=widgets.sh +. ~/.resh/widgets.sh + +__resh_bind_arrows() { + bindfunc '\C-k' __resh_widget_arrow_up_compat + bindfunc '\C-j' __resh_widget_arrow_down_compat + return 0 +} + +__resh_bind_control_R() { + echo "bindfunc __resh_widget_control_R_compat" + return 0 +} +__resh_unbind_arrows() { + echo "\ bindfunc __resh_widget_arrow_up_compat" + echo "\ bindfunc __resh_widget_arrow_down_compat" + return 0 +} + +__resh_unbind_control_R() { + echo "\ bindfunc __resh_widget_control_R_compat" + return 0 +} + +__resh_bind_all() { + __resh_bind_arrows + __resh_bind_control_R +} + +__resh_unbind_all() { + __resh_unbind_arrows + __resh_unbind_control_R +} reshctl() { # run resh-control aka the real reshctl @@ -25,6 +57,11 @@ reshctl() { __resh_unbind_all return 0 ;; + 200) + # reload rc files + . ~/.resh/shellrc + return 0 + ;; *) echo "reshctl() FATAL ERROR: unknown status" >&2 return "$status" diff --git a/scripts/shellrc.sh b/scripts/shellrc.sh index 739c299..865e2fd 100644 --- a/scripts/shellrc.sh +++ b/scripts/shellrc.sh @@ -4,70 +4,13 @@ PATH=$PATH:~/.resh/bin # zmodload zsh/datetime # fi +# shellcheck source=hooks.sh +. ~/.resh/hooks.sh +# shellcheck source=util.sh +. ~/.resh/util.sh # shellcheck source=reshctl.sh . ~/.resh/reshctl.sh -__resh_get_uuid() { - cat /proc/sys/kernel/random/uuid 2>/dev/null || resh-uuid -} - -__resh_get_epochrealtime() { - if date +%s.%N | grep -vq 'N'; then - # GNU date - date +%s.%N - elif gdate --version >/dev/null && gdate +%s.%N | grep -vq 'N'; then - # GNU date take 2 - gdate +%s.%N - elif [ -n "$ZSH_VERSION" ]; then - # zsh fallback using $EPOCHREALTIME - if [ -z "${__RESH_ZSH_LOADED_DATETIME+x}" ]; then - zmodload zsh/datetime - __RESH_ZSH_LOADED_DATETIME=1 - fi - echo "$EPOCHREALTIME" - else - # dumb date - # XXX: we lost precison beyond seconds - date +%s - if [ -z "${__RESH_DATE_WARN+x}" ]; then - echo "resh WARN: can't get precise time - consider installing GNU date!" - __RESH_DATE_WARN=1 - fi - fi -} - -__resh_run_daemon() { - if [ -n "$ZSH_VERSION" ]; then - setopt LOCAL_OPTIONS NO_NOTIFY NO_MONITOR - fi - nohup resh-daemon &>~/.resh/daemon_last_run_out.txt & disown -} - -# __resh_session_init() { -# resh-event session_start --sessionId "$__RESH_SESSION_ID" --sessionPid "$__RESH_SESSION_PID" -# } -# -__resh_bash_completion_init() { - local bash_completion_dir=~/.resh/bash_completion.d - # source user completion directory definitions - # taken from /usr/share/bash-completion/bash_completion - if [[ -d $bash_completion_dir && -r $bash_completion_dir && \ - -x $bash_completion_dir ]]; then - for i in $(LC_ALL=C command ls "$bash_completion_dir"); do - i=$bash_completion_dir/$i - # shellcheck disable=SC2154 - # shellcheck source=/dev/null - [[ ${i##*/} != @($_backup_glob|Makefile*|$_blacklist_glob) \ - && -f $i && -r $i ]] && . "$i" - done - fi -} - -__resh_zsh_completion_init() { - # shellcheck disable=SC2206 - fpath=(~/.resh/zsh_completion.d $fpath) -} - __RESH_MACOS=0 __RESH_LINUX=0 __RESH_UNAME=$(uname) @@ -95,13 +38,6 @@ else echo "resh PANIC unrecognized shell" fi -if [ -z "${__RESH_SESSION_ID+x}" ]; then - export __RESH_SESSION_ID; __RESH_SESSION_ID=$(__resh_get_uuid) - export __RESH_SESSION_PID="$$" - # TODO add sesson time - # __resh_session_init __RESH_SESSION_ID __RESH_SESSION_PID -fi - # posix __RESH_HOME="$HOME" __RESH_LOGIN="$LOGNAME" @@ -133,140 +69,12 @@ __RESH_REVISION=$(resh-collect -revision) __resh_run_daemon -__resh_preexec() { - # core - __RESH_COLLECT=1 - __RESH_CMDLINE="$1" - - # posix - __RESH_COLS="$COLUMNS" - __RESH_LANG="$LANG" - __RESH_LC_ALL="$LC_ALL" - # other LC ? - __RESH_LINES="$LINES" - # __RESH_PATH="$PATH" - __RESH_PWD="$PWD" - - # non-posix - __RESH_SHLVL="$SHLVL" - __RESH_GIT_CDUP="$(git rev-parse --show-cdup 2>/dev/null)" - __RESH_GIT_CDUP_EXIT_CODE=$? - __RESH_GIT_REMOTE="$(git remote get-url origin 2>/dev/null)" - __RESH_GIT_REMOTE_EXIT_CODE=$? - #__RESH_GIT_TOPLEVEL="$(git rev-parse --show-toplevel)" - #__RESH_GIT_TOPLEVEL_EXIT_CODE=$? - - if [ -n "$ZSH_VERSION" ]; then - # assume Zsh - __RESH_PID="$$" # current pid - elif [ -n "$BASH_VERSION" ]; then - # assume Bash - __RESH_PID="$BASHPID" # current pid - fi - # time - __RESH_TZ_BEFORE=$(date +%z) - # __RESH_RT_BEFORE="$EPOCHREALTIME" - __RESH_RT_BEFORE=$(__resh_get_epochrealtime) - - if [ "$__RESH_VERSION" != "$(resh-collect -version)" ]; then - # shellcheck source=shellrc.sh - source ~/.resh/shellrc - if [ "$__RESH_VERSION" != "$(resh-collect -version)" ]; then - echo "RESH WARNING: You probably just updated RESH - PLEASE RESTART OR RELOAD THIS TERMINAL SESSION (resh version: $(resh-collect -version); resh version of this terminal session: ${__RESH_VERSION})" - else - echo "RESH INFO: New RESH shellrc script was loaded - if you encounter any issues please restart this terminal session." - fi - elif [ "$__RESH_REVISION" != "$(resh-collect -revision)" ]; then - # shellcheck source=shellrc.sh - source ~/.resh/shellrc - if [ "$__RESH_REVISION" != "$(resh-collect -revision)" ]; then - echo "RESH WARNING: You probably just updated RESH - PLEASE RESTART OR RELOAD THIS TERMINAL SESSION (resh revision: $(resh-collect -revision); resh revision of this terminal session: ${__RESH_REVISION})" - fi - fi - if [ "$__RESH_VERSION" = "$(resh-collect -version)" ] && [ "$__RESH_REVISION" = "$(resh-collect -revision)" ]; then - resh-collect -requireVersion "$__RESH_VERSION" \ - -requireRevision "$__RESH_REVISION" \ - -cmdLine "$__RESH_CMDLINE" \ - -shell "$__RESH_SHELL" \ - -uname "$__RESH_UNAME" \ - -sessionId "$__RESH_SESSION_ID" \ - -cols "$__RESH_COLS" \ - -home "$__RESH_HOME" \ - -lang "$__RESH_LANG" \ - -lcAll "$__RESH_LC_ALL" \ - -lines "$__RESH_LINES" \ - -login "$__RESH_LOGIN" \ - -pwd "$__RESH_PWD" \ - -shellEnv "$__RESH_SHELL_ENV" \ - -term "$__RESH_TERM" \ - -pid "$__RESH_PID" \ - -sessionPid "$__RESH_SESSION_PID" \ - -host "$__RESH_HOST" \ - -hosttype "$__RESH_HOSTTYPE" \ - -ostype "$__RESH_OSTYPE" \ - -machtype "$__RESH_MACHTYPE" \ - -shlvl "$__RESH_SHLVL" \ - -gitCdup "$__RESH_GIT_CDUP" \ - -gitCdupExitCode "$__RESH_GIT_CDUP_EXIT_CODE" \ - -gitRemote "$__RESH_GIT_REMOTE" \ - -gitRemoteExitCode "$__RESH_GIT_REMOTE_EXIT_CODE" \ - -realtimeBefore "$__RESH_RT_BEFORE" \ - -realtimeSession "$__RESH_RT_SESSION" \ - -realtimeSessSinceBoot "$__RESH_RT_SESS_SINCE_BOOT" \ - -timezoneBefore "$__RESH_TZ_BEFORE" \ - -osReleaseId "$__RESH_OS_RELEASE_ID" \ - -osReleaseVersionId "$__RESH_OS_RELEASE_VERSION_ID" \ - -osReleaseIdLike "$__RESH_OS_RELEASE_ID_LIKE" \ - -osReleaseName "$__RESH_OS_RELEASE_NAME" \ - -osReleasePrettyName "$__RESH_OS_RELEASE_PRETTY_NAME" \ - &>~/.resh/collect_last_run_out.txt || echo "resh-collect ERROR: $(head -n 1 ~/.resh/collect_last_run_out.txt)" - fi -} - -__resh_precmd() { - __RESH_EXIT_CODE=$? - __RESH_RT_AFTER=$(__resh_get_epochrealtime) - __RESH_TZ_AFTER=$(date +%z) - __RESH_PWD_AFTER="$PWD" - __RESH_GIT_CDUP_AFTER="$(git rev-parse --show-cdup 2>/dev/null)" - __RESH_GIT_CDUP_EXIT_CODE_AFTER=$? - __RESH_GIT_REMOTE_AFTER="$(git remote get-url origin 2>/dev/null)" - __RESH_GIT_REMOTE_EXIT_CODE_AFTER=$? - if [ -n "${__RESH_COLLECT}" ]; then - if [ "$__RESH_VERSION" != "$(resh-postcollect -version)" ]; then - # shellcheck source=shellrc.sh - source ~/.resh/shellrc - if [ "$__RESH_VERSION" != "$(resh-postcollect -version)" ]; then - echo "RESH WARNING: You probably just updated RESH - PLEASE RESTART OR RELOAD THIS TERMINAL SESSION (resh version: $(resh-collect -version); resh version of this terminal session: ${__RESH_VERSION})" - else - echo "RESH INFO: New RESH shellrc script was loaded - if you encounter any issues please restart this terminal session." - fi - elif [ "$__RESH_REVISION" != "$(resh-postcollect -revision)" ]; then - # shellcheck source=shellrc.sh - source ~/.resh/shellrc - if [ "$__RESH_REVISION" != "$(resh-postcollect -revision)" ]; then - echo "RESH WARNING: You probably just updated RESH - PLEASE RESTART OR RELOAD THIS TERMINAL SESSION (resh revision: $(resh-collect -revision); resh revision of this terminal session: ${__RESH_REVISION})" - fi - fi - if [ "$__RESH_VERSION" = "$(resh-postcollect -version)" ] && [ "$__RESH_REVISION" = "$(resh-postcollect -revision)" ]; then - resh-postcollect -requireVersion "$__RESH_VERSION" \ - -requireRevision "$__RESH_REVISION" \ - -cmdLine "$__RESH_CMDLINE" \ - -realtimeBefore "$__RESH_RT_BEFORE" \ - -exitCode "$__RESH_EXIT_CODE" \ - -sessionId "$__RESH_SESSION_ID" \ - -pwdAfter "$__RESH_PWD_AFTER" \ - -gitCdupAfter "$__RESH_GIT_CDUP_AFTER" \ - -gitCdupExitCodeAfter "$__RESH_GIT_CDUP_EXIT_CODE_AFTER" \ - -gitRemoteAfter "$__RESH_GIT_REMOTE_AFTER" \ - -gitRemoteExitCodeAfter "$__RESH_GIT_REMOTE_EXIT_CODE_AFTER" \ - -realtimeAfter "$__RESH_RT_AFTER" \ - -timezoneAfter "$__RESH_TZ_AFTER" \ - &>~/.resh/postcollect_last_run_out.txt || echo "resh-postcollect ERROR: $(head -n 1 ~/.resh/postcollect_last_run_out.txt)" - fi - fi - unset __RESH_COLLECT -} +if [ -z "${__RESH_SESSION_ID+x}" ]; then + export __RESH_SESSION_ID; __RESH_SESSION_ID=$(__resh_get_uuid) + export __RESH_SESSION_PID="$$" + # TODO add sesson time + __resh_session_init +fi # do not add more hooks when shellrc is sourced again if [ -z "${__RESH_PREEXEC_PRECMD_HOOKS_ADDED+x}" ]; then diff --git a/scripts/util.sh b/scripts/util.sh new file mode 100644 index 0000000..bbdceb4 --- /dev/null +++ b/scripts/util.sh @@ -0,0 +1,136 @@ +# util.sh - resh utility functions +__resh_get_uuid() { + cat /proc/sys/kernel/random/uuid 2>/dev/null || resh-uuid +} + +__resh_get_pid() { + if [ -n "$ZSH_VERSION" ]; then + # assume Zsh + local __RESH_PID="$$" # current pid + elif [ -n "$BASH_VERSION" ]; then + # assume Bash + local __RESH_PID="$BASHPID" # current pid + fi + echo "$__RESH_PID" +} + +__resh_get_epochrealtime() { + if date +%s.%N | grep -vq 'N'; then + # GNU date + date +%s.%N + elif gdate --version >/dev/null && gdate +%s.%N | grep -vq 'N'; then + # GNU date take 2 + gdate +%s.%N + elif [ -n "$ZSH_VERSION" ]; then + # zsh fallback using $EPOCHREALTIME + if [ -z "${__RESH_ZSH_LOADED_DATETIME+x}" ]; then + zmodload zsh/datetime + __RESH_ZSH_LOADED_DATETIME=1 + fi + echo "$EPOCHREALTIME" + else + # dumb date + # XXX: we lost precison beyond seconds + date +%s + if [ -z "${__RESH_DATE_WARN+x}" ]; then + echo "resh WARN: can't get precise time - consider installing GNU date!" + __RESH_DATE_WARN=1 + fi + fi +} + +__resh_run_daemon() { + if [ -n "$ZSH_VERSION" ]; then + setopt LOCAL_OPTIONS NO_NOTIFY NO_MONITOR + fi + nohup resh-daemon &>~/.resh/daemon_last_run_out.txt & disown +} + +__resh_bash_completion_init() { + local bash_completion_dir=~/.resh/bash_completion.d + # source user completion directory definitions + # taken from /usr/share/bash-completion/bash_completion + if [[ -d $bash_completion_dir && -r $bash_completion_dir && \ + -x $bash_completion_dir ]]; then + for i in $(LC_ALL=C command ls "$bash_completion_dir"); do + i=$bash_completion_dir/$i + # shellcheck disable=SC2154 + # shellcheck source=/dev/null + [[ ${i##*/} != @($_backup_glob|Makefile*|$_blacklist_glob) \ + && -f $i && -r $i ]] && . "$i" + done + fi +} + +__resh_zsh_completion_init() { + # shellcheck disable=SC2206 + fpath=(~/.resh/zsh_completion.d $fpath) +} + +__resh_session_init() { + # posix + local __RESH_COLS="$COLUMNS" + local __RESH_LANG="$LANG" + local __RESH_LC_ALL="$LC_ALL" + # other LC ? + local __RESH_LINES="$LINES" + local __RESH_PWD="$PWD" + + # non-posix + local __RESH_SHLVL="$SHLVL" + + # pid + local __RESH_PID; __RESH_PID=$(__resh_get_pid) + + # time + local __RESH_TZ_BEFORE; __RESH_TZ_BEFORE=$(date +%z) + local __RESH_RT_BEFORE; __RESH_RT_BEFORE=$(__resh_get_epochrealtime) + + if [ "$__RESH_VERSION" != "$(resh-session-init -version)" ]; then + # shellcheck source=shellrc.sh + source ~/.resh/shellrc + if [ "$__RESH_VERSION" != "$(resh-session-init -version)" ]; then + echo "RESH WARNING: You probably just updated RESH - PLEASE RESTART OR RELOAD THIS TERMINAL SESSION (resh version: $(resh-session-init -version); resh version of this terminal session: ${__RESH_VERSION})" + else + echo "RESH INFO: New RESH shellrc script was loaded - if you encounter any issues please restart this terminal session." + fi + elif [ "$__RESH_REVISION" != "$(resh-session-init -revision)" ]; then + # shellcheck source=shellrc.sh + source ~/.resh/shellrc + if [ "$__RESH_REVISION" != "$(resh-session-init -revision)" ]; then + echo "RESH WARNING: You probably just updated RESH - PLEASE RESTART OR RELOAD THIS TERMINAL SESSION (resh revision: $(resh-session-init -revision); resh revision of this terminal session: ${__RESH_REVISION})" + fi + fi + if [ "$__RESH_VERSION" = "$(resh-session-init -version)" ] && [ "$__RESH_REVISION" = "$(resh-session-init -revision)" ]; then + resh-session-init -requireVersion "$__RESH_VERSION" \ + -requireRevision "$__RESH_REVISION" \ + -shell "$__RESH_SHELL" \ + -uname "$__RESH_UNAME" \ + -sessionId "$__RESH_SESSION_ID" \ + -cols "$__RESH_COLS" \ + -home "$__RESH_HOME" \ + -lang "$__RESH_LANG" \ + -lcAll "$__RESH_LC_ALL" \ + -lines "$__RESH_LINES" \ + -login "$__RESH_LOGIN" \ + -shellEnv "$__RESH_SHELL_ENV" \ + -term "$__RESH_TERM" \ + -pid "$__RESH_PID" \ + -sessionPid "$__RESH_SESSION_PID" \ + -host "$__RESH_HOST" \ + -hosttype "$__RESH_HOSTTYPE" \ + -ostype "$__RESH_OSTYPE" \ + -machtype "$__RESH_MACHTYPE" \ + -shlvl "$__RESH_SHLVL" \ + -realtimeBefore "$__RESH_RT_BEFORE" \ + -realtimeSession "$__RESH_RT_SESSION" \ + -realtimeSessSinceBoot "$__RESH_RT_SESS_SINCE_BOOT" \ + -timezoneBefore "$__RESH_TZ_BEFORE" \ + -osReleaseId "$__RESH_OS_RELEASE_ID" \ + -osReleaseVersionId "$__RESH_OS_RELEASE_VERSION_ID" \ + -osReleaseIdLike "$__RESH_OS_RELEASE_ID_LIKE" \ + -osReleaseName "$__RESH_OS_RELEASE_NAME" \ + -osReleasePrettyName "$__RESH_OS_RELEASE_PRETTY_NAME" \ + &>~/.resh/session_init_last_run_out.txt || echo "resh-session-init ERROR: $(head -n 1 ~/.resh/session_init_last_run_out.txt)" + fi +} diff --git a/scripts/widgets.sh b/scripts/widgets.sh index c6cea7d..77a7306 100644 --- a/scripts/widgets.sh +++ b/scripts/widgets.sh @@ -1,10 +1,28 @@ +# shellcheck source=hooks.sh +. ~/.resh/hooks.sh + __resh_widget_arrow_up() { - return 0 + (( __RESH_HISTNO++ )) + BUFFER="$(__resh_collect --recall --histno "$__RESH_HISTNO" 2> ~/.resh/arrow_up_last_run_out.txt)" } __resh_widget_arrow_down() { - return 0 + (( __RESH_HISTNO-- )) + BUFFER="$(__resh_collect --recall --histno "$__RESH_HISTNO" 2> ~/.resh/arrow_down_last_run_out.txt)" } __resh_widget_control_R() { - return 0 -} \ No newline at end of file + # resh-collect --hstr + hstr +} + +__resh_widget_arrow_up_compat() { + __bindfunc_compat_wrapper __resh_widget_arrow_up +} + +__resh_widget_arrow_down_compat() { + __bindfunc_compat_wrapper __resh_widget_arrow_down +} + +__resh_widget_control_R_compat() { + __bindfunc_compat_wrapper __resh_widget_control_R +}