tmux.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. // License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
  2. package tui
  3. import (
  4. "errors"
  5. "fmt"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. "strconv"
  10. "strings"
  11. "sync"
  12. "time"
  13. "github.com/shirou/gopsutil/v3/process"
  14. "golang.org/x/sys/unix"
  15. "kitty/tools/utils"
  16. )
  17. var _ = fmt.Print
  18. var TmuxExe = sync.OnceValue(func() string {
  19. return utils.FindExe("tmux")
  20. })
  21. func tmux_socket_address() (socket string) {
  22. socket = os.Getenv("TMUX")
  23. if socket == "" {
  24. return ""
  25. }
  26. addr, pid_str, found := strings.Cut(socket, ",")
  27. if !found {
  28. return ""
  29. }
  30. if unix.Access(addr, unix.R_OK|unix.W_OK) != nil {
  31. return ""
  32. }
  33. pid_str, _, _ = strings.Cut(pid_str, ",")
  34. pid, err := strconv.ParseInt(pid_str, 10, 32)
  35. if err != nil {
  36. return ""
  37. }
  38. p, err := process.NewProcess(int32(pid))
  39. if err != nil {
  40. return ""
  41. }
  42. cmd, err := p.CmdlineSlice()
  43. if err != nil {
  44. return ""
  45. }
  46. if len(cmd) > 0 && strings.ToLower(filepath.Base(cmd[0])) != "tmux" {
  47. return ""
  48. }
  49. return socket
  50. }
  51. var TmuxSocketAddress = sync.OnceValue(tmux_socket_address)
  52. func tmux_command(args ...string) (c *exec.Cmd, stderr *strings.Builder) {
  53. c = exec.Command(TmuxExe(), args...)
  54. stderr = &strings.Builder{}
  55. c.Stderr = stderr
  56. return c, stderr
  57. }
  58. func tmux_allow_passthrough() error {
  59. c, stderr := tmux_command("show", "-Ap", "allow-passthrough")
  60. allowed, not_allowed := errors.New("allowed"), errors.New("not allowed")
  61. get_result := make(chan error)
  62. go func() {
  63. output, err := c.Output()
  64. if err != nil {
  65. get_result <- fmt.Errorf("Running %#v failed with error: %w. STDERR: %s", c.Args, err, stderr.String())
  66. } else {
  67. q := strings.TrimSpace(utils.UnsafeBytesToString(output))
  68. if strings.HasSuffix(q, " on") || strings.HasSuffix(q, " all") {
  69. get_result <- allowed
  70. } else {
  71. get_result <- not_allowed
  72. }
  73. }
  74. }()
  75. select {
  76. case r := <-get_result:
  77. if r == allowed {
  78. return nil
  79. }
  80. if r != not_allowed {
  81. return r
  82. }
  83. c, stderr = tmux_command("set", "-p", "allow-passthrough", "on")
  84. err := c.Run()
  85. if err != nil {
  86. err = fmt.Errorf("Running %#v failed with error: %w. STDERR: %s", c.Args, err, stderr.String())
  87. }
  88. return err
  89. case <-time.After(2 * time.Second):
  90. return fmt.Errorf("Tmux command timed out. This often happens when the version of tmux on your PATH is older than the version of the running tmux server")
  91. }
  92. }
  93. var TmuxAllowPassthrough = sync.OnceValue(tmux_allow_passthrough)