From d19fc375bac34917b62b92f214502bf4a750ca24 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 Nov 2025 12:01:05 +0530 Subject: [PATCH] Switch to external shm package --- go.mod | 1 + go.sum | 2 + kittens/icat/detect.go | 2 +- kittens/icat/transmit.go | 2 +- kittens/ssh/askpass.go | 2 +- kittens/ssh/main.go | 2 +- kittens/ssh/main_test.go | 2 +- tools/cmd/atexit/main.go | 2 +- tools/cmd/pytest/main.go | 47 ++++- tools/tui/graphics/collection.go | 2 +- tools/utils/images/loading.go | 2 +- tools/utils/shm/fallocate_linux.go | 20 -- tools/utils/shm/fallocate_other.go | 16 -- tools/utils/shm/shm.go | 252 -------------------------- tools/utils/shm/shm_fs.go | 189 ------------------- tools/utils/shm/shm_syscall.go | 200 -------------------- tools/utils/shm/shm_test.go | 61 ------- tools/utils/shm/specific_darwin.go | 7 - tools/utils/shm/specific_dragonfly.go | 12 -- tools/utils/shm/specific_freebsd.go | 7 - tools/utils/shm/specific_linux.go | 11 -- tools/utils/shm/specific_netbsd.go | 11 -- tools/utils/shm/specific_openbsd.go | 11 -- 23 files changed, 56 insertions(+), 807 deletions(-) delete mode 100644 tools/utils/shm/fallocate_linux.go delete mode 100644 tools/utils/shm/fallocate_other.go delete mode 100644 tools/utils/shm/shm.go delete mode 100644 tools/utils/shm/shm_fs.go delete mode 100644 tools/utils/shm/shm_syscall.go delete mode 100644 tools/utils/shm/shm_test.go delete mode 100644 tools/utils/shm/specific_darwin.go delete mode 100644 tools/utils/shm/specific_dragonfly.go delete mode 100644 tools/utils/shm/specific_freebsd.go delete mode 100644 tools/utils/shm/specific_linux.go delete mode 100644 tools/utils/shm/specific_netbsd.go delete mode 100644 tools/utils/shm/specific_openbsd.go diff --git a/go.mod b/go.mod index d5494d8a1..b7ff3a145 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/google/uuid v1.6.0 github.com/kovidgoyal/dbus v0.0.0-20250519011319-e811c41c0bc1 github.com/kovidgoyal/go-parallel v1.1.1 + github.com/kovidgoyal/go-shm v1.0.0 github.com/kovidgoyal/imaging v1.8.8 github.com/seancfoley/ipaddress-go v1.7.1 github.com/shirou/gopsutil/v4 v4.25.10 diff --git a/go.sum b/go.sum index 01257683f..1a5d4acb2 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,8 @@ github.com/kovidgoyal/dbus v0.0.0-20250519011319-e811c41c0bc1 h1:rMY/hWfcVzBm6BL github.com/kovidgoyal/dbus v0.0.0-20250519011319-e811c41c0bc1/go.mod h1:RbNG3Q1g6GUy1/WzWVx+S24m7VKyvl57vV+cr2hpt50= github.com/kovidgoyal/go-parallel v1.1.1 h1:1OzpNjtrUkBPq3UaqrnvOoB2F9RttSt811uiUXyI7ok= github.com/kovidgoyal/go-parallel v1.1.1/go.mod h1:BJNIbe6+hxyFWv7n6oEDPj3PA5qSw5OCtf0hcVxWJiw= +github.com/kovidgoyal/go-shm v1.0.0 h1:HJEel9D1F9YhULvClEHJLawoRSj/1u/EDV7MJbBPgQo= +github.com/kovidgoyal/go-shm v1.0.0/go.mod h1:Yzb80Xf9L3kaoB2RGok9hHwMIt7Oif61kT6t3+VnZds= github.com/kovidgoyal/imaging v1.8.8 h1:PohlAOYuokFtmt6sjhgA90YAUKhuuL3i0dhd5gepp4g= github.com/kovidgoyal/imaging v1.8.8/go.mod h1:GAbZkbyB86PSfosof5EnS2o6N15yUk9Vy2r61EWy1Wg= github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik= diff --git a/kittens/icat/detect.go b/kittens/icat/detect.go index 226a600d5..9d19d5cda 100644 --- a/kittens/icat/detect.go +++ b/kittens/icat/detect.go @@ -8,11 +8,11 @@ import ( "os" "time" + "github.com/kovidgoyal/go-shm" "github.com/kovidgoyal/kitty/tools/tui/graphics" "github.com/kovidgoyal/kitty/tools/tui/loop" "github.com/kovidgoyal/kitty/tools/utils" "github.com/kovidgoyal/kitty/tools/utils/images" - "github.com/kovidgoyal/kitty/tools/utils/shm" ) var _ = fmt.Print diff --git a/kittens/icat/transmit.go b/kittens/icat/transmit.go index c6f92a81f..4da2746f7 100644 --- a/kittens/icat/transmit.go +++ b/kittens/icat/transmit.go @@ -15,12 +15,12 @@ import ( "path/filepath" "strings" + "github.com/kovidgoyal/go-shm" "github.com/kovidgoyal/kitty/tools/tui" "github.com/kovidgoyal/kitty/tools/tui/graphics" "github.com/kovidgoyal/kitty/tools/tui/loop" "github.com/kovidgoyal/kitty/tools/utils" "github.com/kovidgoyal/kitty/tools/utils/images" - "github.com/kovidgoyal/kitty/tools/utils/shm" ) var _ = fmt.Print diff --git a/kittens/ssh/askpass.go b/kittens/ssh/askpass.go index ac28035ec..742dd9c78 100644 --- a/kittens/ssh/askpass.go +++ b/kittens/ssh/askpass.go @@ -13,9 +13,9 @@ import ( "strings" "time" + "github.com/kovidgoyal/go-shm" "github.com/kovidgoyal/kitty/tools/cli" "github.com/kovidgoyal/kitty/tools/tty" - "github.com/kovidgoyal/kitty/tools/utils/shm" ) var _ = fmt.Print diff --git a/kittens/ssh/main.go b/kittens/ssh/main.go index 438c47910..0eb90f4c0 100644 --- a/kittens/ssh/main.go +++ b/kittens/ssh/main.go @@ -28,6 +28,7 @@ import ( "syscall" "time" + "github.com/kovidgoyal/go-shm" "github.com/kovidgoyal/kitty/tools/cli" "github.com/kovidgoyal/kitty/tools/themes" "github.com/kovidgoyal/kitty/tools/tty" @@ -37,7 +38,6 @@ import ( "github.com/kovidgoyal/kitty/tools/utils" "github.com/kovidgoyal/kitty/tools/utils/secrets" "github.com/kovidgoyal/kitty/tools/utils/shlex" - "github.com/kovidgoyal/kitty/tools/utils/shm" "golang.org/x/sys/unix" ) diff --git a/kittens/ssh/main_test.go b/kittens/ssh/main_test.go index 0e1a83a29..21fcee8d2 100644 --- a/kittens/ssh/main_test.go +++ b/kittens/ssh/main_test.go @@ -6,8 +6,8 @@ import ( "encoding/binary" "encoding/json" "fmt" + "github.com/kovidgoyal/go-shm" "github.com/kovidgoyal/kitty" - "github.com/kovidgoyal/kitty/tools/utils/shm" "io/fs" "os" "os/exec" diff --git a/tools/cmd/atexit/main.go b/tools/cmd/atexit/main.go index 5fd527655..eb108ff77 100644 --- a/tools/cmd/atexit/main.go +++ b/tools/cmd/atexit/main.go @@ -7,9 +7,9 @@ import ( "os/signal" "strings" + "github.com/kovidgoyal/go-shm" "github.com/kovidgoyal/kitty/tools/cli" "github.com/kovidgoyal/kitty/tools/utils" - "github.com/kovidgoyal/kitty/tools/utils/shm" ) var _ = fmt.Print diff --git a/tools/cmd/pytest/main.go b/tools/cmd/pytest/main.go index 08b4c3e85..fea1742c1 100644 --- a/tools/cmd/pytest/main.go +++ b/tools/cmd/pytest/main.go @@ -4,19 +4,62 @@ package pytest import ( "fmt" + "io" + "os" + "github.com/kovidgoyal/go-shm" "github.com/kovidgoyal/kitty/kittens/ssh" "github.com/kovidgoyal/kitty/tools/cli" - "github.com/kovidgoyal/kitty/tools/utils/shm" ) var _ = fmt.Print +func test_integration_with_python(args []string) (rc int, err error) { + switch args[0] { + default: + return 1, fmt.Errorf("Unknown test type: %s", args[0]) + case "read": + data, err := shm.ReadWithSizeAndUnlink(args[1]) + if err != nil { + return 1, err + } + _, err = os.Stdout.Write(data) + if err != nil { + return 1, err + } + case "write": + data, err := io.ReadAll(os.Stdin) + if err != nil { + return 1, err + } + mmap, err := shm.CreateTemp("shmtest-", uint64(len(data)+shm.NUM_BYTES_FOR_SIZE)) + if err != nil { + return 1, err + } + if err = shm.WriteWithSize(mmap, data, 0); err != nil { + return 1, err + } + mmap.Close() + fmt.Println(mmap.Name()) + } + return 0, nil +} + +func shm_entry_point(root *cli.Command) { + root.AddSubCommand(&cli.Command{ + Name: "shm", + OnlyArgsAllowed: true, + Run: func(cmd *cli.Command, args []string) (rc int, err error) { + return test_integration_with_python(args) + }, + }) + +} func EntryPoint(root *cli.Command) { root = root.AddSubCommand(&cli.Command{ Name: "__pytest__", Hidden: true, }) - shm.TestEntryPoint(root) + shm_entry_point(root) ssh.TestEntryPoint(root) } diff --git a/tools/tui/graphics/collection.go b/tools/tui/graphics/collection.go index 0e77af35a..f1542ea5b 100644 --- a/tools/tui/graphics/collection.go +++ b/tools/tui/graphics/collection.go @@ -10,11 +10,11 @@ import ( "sync" "sync/atomic" + "github.com/kovidgoyal/go-shm" "github.com/kovidgoyal/kitty/tools/tui" "github.com/kovidgoyal/kitty/tools/tui/loop" "github.com/kovidgoyal/kitty/tools/utils" "github.com/kovidgoyal/kitty/tools/utils/images" - "github.com/kovidgoyal/kitty/tools/utils/shm" ) var _ = fmt.Print diff --git a/tools/utils/images/loading.go b/tools/utils/images/loading.go index 73afb204a..0a8f18cdd 100644 --- a/tools/utils/images/loading.go +++ b/tools/utils/images/loading.go @@ -10,9 +10,9 @@ import ( "os" "strings" + "github.com/kovidgoyal/go-shm" "github.com/kovidgoyal/imaging/nrgb" "github.com/kovidgoyal/kitty/tools/utils" - "github.com/kovidgoyal/kitty/tools/utils/shm" "github.com/kovidgoyal/imaging" ) diff --git a/tools/utils/shm/fallocate_linux.go b/tools/utils/shm/fallocate_linux.go deleted file mode 100644 index 8cd91b68d..000000000 --- a/tools/utils/shm/fallocate_linux.go +++ /dev/null @@ -1,20 +0,0 @@ -// License: GPLv3 Copyright: 2023, Kovid Goyal, - -package shm - -import ( - "errors" - "fmt" - - "golang.org/x/sys/unix" -) - -var _ = fmt.Print - -func Fallocate_simple(fd int, size int64) (err error) { - for { - if err = unix.Fallocate(fd, 0, 0, size); !errors.Is(err, unix.EINTR) { - return - } - } -} diff --git a/tools/utils/shm/fallocate_other.go b/tools/utils/shm/fallocate_other.go deleted file mode 100644 index 3aa7ee532..000000000 --- a/tools/utils/shm/fallocate_other.go +++ /dev/null @@ -1,16 +0,0 @@ -// License: GPLv3 Copyright: 2023, Kovid Goyal, - -//go:build !linux - -package shm - -import ( - "errors" - "fmt" -) - -var _ = fmt.Print - -func Fallocate_simple(fd int, size int64) (err error) { - return errors.ErrUnsupported -} diff --git a/tools/utils/shm/shm.go b/tools/utils/shm/shm.go deleted file mode 100644 index a01f1251c..000000000 --- a/tools/utils/shm/shm.go +++ /dev/null @@ -1,252 +0,0 @@ -// License: GPLv3 Copyright: 2022, Kovid Goyal, - -package shm - -import ( - "encoding/binary" - "errors" - "fmt" - "io" - "io/fs" - "os" - "strings" - - "github.com/kovidgoyal/kitty/tools/cli" - - "golang.org/x/sys/unix" -) - -var _ = fmt.Print -var ErrPatternHasSeparator = errors.New("The specified pattern has file path separators in it") -var ErrPatternTooLong = errors.New("The specified pattern for the SHM name is too long") - -type ErrNotSupported struct { - err error -} - -func (self *ErrNotSupported) Error() string { - return fmt.Sprintf("POSIX shared memory not supported on this platform: with underlying error: %v", self.err) -} - -// prefix_and_suffix splits pattern by the last wildcard "*", if applicable, -// returning prefix as the part before "*" and suffix as the part after "*". -func prefix_and_suffix(pattern string) (prefix, suffix string, err error) { - for i := 0; i < len(pattern); i++ { - if os.IsPathSeparator(pattern[i]) { - return "", "", ErrPatternHasSeparator - } - } - if pos := strings.LastIndexByte(pattern, '*'); pos != -1 { - prefix, suffix = pattern[:pos], pattern[pos+1:] - } else { - prefix = pattern - } - return prefix, suffix, nil -} - -type MMap interface { - Close() error - Unlink() error - Slice() []byte - Name() string - IsFileSystemBacked() bool - FileSystemName() string - Stat() (fs.FileInfo, error) - Flush() error - Seek(offset int64, whence int) (ret int64, err error) - Read(b []byte) (n int, err error) - Write(b []byte) (n int, err error) -} - -type AccessFlags int - -const ( - READ AccessFlags = iota - WRITE - COPY -) - -func mmap(sz int, access AccessFlags, fd int, off int64) ([]byte, error) { - flags := unix.MAP_SHARED - prot := unix.PROT_READ - switch access { - case COPY: - prot |= unix.PROT_WRITE - flags = unix.MAP_PRIVATE - case WRITE: - prot |= unix.PROT_WRITE - } - - b, err := unix.Mmap(fd, off, sz, prot, flags) - if err != nil { - return nil, err - } - return b, nil -} - -func munmap(s []byte) error { - return unix.Munmap(s) -} - -func CreateTemp(pattern string, size uint64) (MMap, error) { - return create_temp(pattern, size) -} - -func truncate_or_unlink(ans *os.File, size uint64, unlink func(string) error) (err error) { - fd := int(ans.Fd()) - sz := int64(size) - if err = Fallocate_simple(fd, sz); err != nil { - if !errors.Is(err, errors.ErrUnsupported) { - return fmt.Errorf("fallocate() failed on fd from shm_open(%s) with size: %d with error: %w", ans.Name(), size, err) - } - for { - if err = unix.Ftruncate(fd, sz); !errors.Is(err, unix.EINTR) { - break - } - } - } - if err != nil { - _ = ans.Close() - _ = unlink(ans.Name()) - return fmt.Errorf("Failed to ftruncate() SHM file %s to size: %d with error: %w", ans.Name(), size, err) - } - return -} - -const NUM_BYTES_FOR_SIZE = 4 - -var ErrRegionTooSmall = errors.New("mmaped region too small") - -func WriteWithSize(self MMap, b []byte, at int) error { - if len(self.Slice()) < at+len(b)+NUM_BYTES_FOR_SIZE { - return ErrRegionTooSmall - } - binary.BigEndian.PutUint32(self.Slice()[at:], uint32(len(b))) - copy(self.Slice()[at+NUM_BYTES_FOR_SIZE:], b) - return nil -} - -func ReadWithSize(self MMap, at int) ([]byte, error) { - s := self.Slice()[at:] - if len(s) < NUM_BYTES_FOR_SIZE { - return nil, ErrRegionTooSmall - } - size := int(binary.BigEndian.Uint32(self.Slice()[at : at+NUM_BYTES_FOR_SIZE])) - s = s[NUM_BYTES_FOR_SIZE:] - if len(s) < size { - return nil, ErrRegionTooSmall - } - return s[:size], nil -} - -func ReadWithSizeAndUnlink(name string, file_callback ...func(fs.FileInfo) error) ([]byte, error) { - mmap, err := Open(name, 0) - if err != nil { - return nil, err - } - if len(file_callback) > 0 { - s, err := mmap.Stat() - if err != nil { - return nil, fmt.Errorf("Failed to stat SHM file with error: %w", err) - } - for _, f := range file_callback { - err = f(s) - if err != nil { - return nil, err - } - } - } - defer func() { - mmap.Close() - _ = mmap.Unlink() - }() - slice, err := ReadWithSize(mmap, 0) - if err != nil { - return nil, err - } - ans := make([]byte, len(slice)) - copy(ans, slice) - return ans, nil -} - -func Read(self MMap, b []byte) (n int, err error) { - pos, err := self.Seek(0, io.SeekCurrent) - if err != nil { - return 0, err - } - if pos < 0 { - pos = 0 - } - s := self.Slice() - sz := int64(len(s)) - if pos >= sz { - return 0, io.EOF - } - n = copy(b, s[pos:]) - _, err = self.Seek(int64(n), io.SeekCurrent) - return -} - -func Write(self MMap, b []byte) (n int, err error) { - if len(b) == 0 { - return 0, nil - } - pos, _ := self.Seek(0, io.SeekCurrent) - if pos < 0 { - pos = 0 - } - s := self.Slice() - if pos >= int64(len(s)) { - return 0, io.ErrShortWrite - } - n = copy(s[pos:], b) - if _, err = self.Seek(int64(n), io.SeekCurrent); err != nil { - return n, err - } - if n < len(b) { - return n, io.ErrShortWrite - } - return n, nil -} - -func test_integration_with_python(args []string) (rc int, err error) { - switch args[0] { - default: - return 1, fmt.Errorf("Unknown test type: %s", args[0]) - case "read": - data, err := ReadWithSizeAndUnlink(args[1]) - if err != nil { - return 1, err - } - _, err = os.Stdout.Write(data) - if err != nil { - return 1, err - } - case "write": - data, err := io.ReadAll(os.Stdin) - if err != nil { - return 1, err - } - mmap, err := CreateTemp("shmtest-", uint64(len(data)+NUM_BYTES_FOR_SIZE)) - if err != nil { - return 1, err - } - if err = WriteWithSize(mmap, data, 0); err != nil { - return 1, err - } - mmap.Close() - fmt.Println(mmap.Name()) - } - return 0, nil -} - -func TestEntryPoint(root *cli.Command) { - root.AddSubCommand(&cli.Command{ - Name: "shm", - OnlyArgsAllowed: true, - Run: func(cmd *cli.Command, args []string) (rc int, err error) { - return test_integration_with_python(args) - }, - }) - -} diff --git a/tools/utils/shm/shm_fs.go b/tools/utils/shm/shm_fs.go deleted file mode 100644 index a0d92abe8..000000000 --- a/tools/utils/shm/shm_fs.go +++ /dev/null @@ -1,189 +0,0 @@ -// License: GPLv3 Copyright: 2022, Kovid Goyal, -//go:build linux || netbsd || openbsd || dragonfly - -package shm - -import ( - "crypto/sha256" - "errors" - "fmt" - "io" - "io/fs" - "os" - "path/filepath" - "runtime" - "strings" - - "github.com/kovidgoyal/kitty/tools/utils" - - "golang.org/x/sys/unix" -) - -var _ = fmt.Print - -type file_based_mmap struct { - f *os.File - pos int64 - region []byte - unlinked bool - special_name string -} - -func ShmUnlink(name string) error { - if runtime.GOOS == "openbsd" { - return os.Remove(openbsd_shm_path(name)) - } - name = strings.TrimPrefix(name, "/") - return os.Remove(filepath.Join(SHM_DIR, name)) -} - -func file_mmap(f *os.File, size uint64, access AccessFlags, truncate bool, special_name string) (MMap, error) { - if truncate { - err := truncate_or_unlink(f, size, os.Remove) - if err != nil { - return nil, err - } - } - region, err := mmap(int(size), access, int(f.Fd()), 0) - if err != nil { - f.Close() - os.Remove(f.Name()) - return nil, err - } - return &file_based_mmap{f: f, region: region, special_name: special_name}, nil -} - -func (self *file_based_mmap) Seek(offset int64, whence int) (ret int64, err error) { - switch whence { - case io.SeekStart: - self.pos = offset - case io.SeekEnd: - self.pos = int64(len(self.region)) + offset - case io.SeekCurrent: - self.pos += offset - } - return self.pos, nil -} - -func (self *file_based_mmap) Read(b []byte) (n int, err error) { - return Read(self, b) -} - -func (self *file_based_mmap) Write(b []byte) (n int, err error) { - return Write(self, b) -} - -func (self *file_based_mmap) Stat() (fs.FileInfo, error) { - return self.f.Stat() -} - -func (self *file_based_mmap) Name() string { - if self.special_name != "" { - return self.special_name - } - return filepath.Base(self.f.Name()) -} - -func (self *file_based_mmap) Flush() error { - return unix.Msync(self.region, unix.MS_SYNC) -} - -func (self *file_based_mmap) FileSystemName() string { - return self.f.Name() -} - -func (self *file_based_mmap) Slice() []byte { - return self.region -} - -func (self *file_based_mmap) Close() (err error) { - if self.region != nil { - self.f.Close() - err = munmap(self.region) - self.region = nil - } - return err -} - -func (self *file_based_mmap) Unlink() (err error) { - if self.unlinked { - return nil - } - self.unlinked = true - return os.Remove(self.f.Name()) -} - -func (self *file_based_mmap) IsFileSystemBacked() bool { return true } - -func openbsd_shm_path(name string) string { - hash := sha256.Sum256(utils.UnsafeStringToBytes(name)) - return filepath.Join(SHM_DIR, utils.UnsafeBytesToString(hash[:])+".shm") -} - -func file_path_from_name(name string) string { - // See https://github.com/openbsd/src/blob/master/lib/libc/gen/shm_open.c - if runtime.GOOS == "openbsd" { - return openbsd_shm_path(name) - } - return filepath.Join(SHM_DIR, name) -} - -func create_temp(pattern string, size uint64) (ans MMap, err error) { - special_name := "" - var prefix, suffix string - prefix, suffix, err = prefix_and_suffix(pattern) - if err != nil { - return - } - var f *os.File - try := 0 - for { - name := prefix + utils.RandomFilename() + suffix - path := file_path_from_name(name) - f, err = os.OpenFile(path, os.O_EXCL|os.O_CREATE|os.O_RDWR, 0600) - if err != nil { - if errors.Is(err, fs.ErrExist) { - try += 1 - if try > 10000 { - return nil, &os.PathError{Op: "createtemp", Path: prefix + "*" + suffix, Err: fs.ErrExist} - } - continue - } - if errors.Is(err, fs.ErrNotExist) { - return nil, &ErrNotSupported{err: err} - } - return - } - break - } - return file_mmap(f, size, WRITE, true, special_name) -} - -func open(name string) (*os.File, error) { - ans, err := os.OpenFile(file_path_from_name(name), os.O_RDONLY, 0) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - if _, serr := os.Stat(SHM_DIR); serr != nil && errors.Is(serr, fs.ErrNotExist) { - return nil, &ErrNotSupported{err: serr} - } - } - return nil, err - } - return ans, nil -} - -func Open(name string, size uint64) (MMap, error) { - ans, err := open(name) - if err != nil { - return nil, err - } - if size == 0 { - s, err := ans.Stat() - if err != nil { - ans.Close() - return nil, fmt.Errorf("Failed to stat SHM file with error: %w", err) - } - size = uint64(s.Size()) - } - return file_mmap(ans, size, READ, false, name) -} diff --git a/tools/utils/shm/shm_syscall.go b/tools/utils/shm/shm_syscall.go deleted file mode 100644 index ea0e24479..000000000 --- a/tools/utils/shm/shm_syscall.go +++ /dev/null @@ -1,200 +0,0 @@ -// License: GPLv3 Copyright: 2022, Kovid Goyal, -//go:build darwin || freebsd - -package shm - -import ( - "errors" - "fmt" - "io" - "io/fs" - "os" - "strings" - "unsafe" - - "github.com/kovidgoyal/kitty/tools/utils" - - "golang.org/x/sys/unix" -) - -var _ = fmt.Print - -// ByteSliceFromString makes a zero terminated byte slice from the string -func ByteSliceFromString(s string) []byte { - a := make([]byte, len(s)+1) - copy(a, s) - return a -} - -func BytePtrFromString(s string) *byte { - a := ByteSliceFromString(s) - return &a[0] -} - -func shm_unlink(name string) (err error) { - bname := BytePtrFromString(name) - for { - _, _, errno := unix.Syscall(unix.SYS_SHM_UNLINK, uintptr(unsafe.Pointer(bname)), 0, 0) - if errno != unix.EINTR { - if errno != 0 { - if errno == unix.ENOENT { - err = fs.ErrNotExist - } else { - err = fmt.Errorf("shm_unlink() failed with error: %w", errno) - } - } - break - } - } - return -} - -func ShmUnlink(name string) error { - return shm_unlink(name) -} - -func shm_open(name string, flags, perm int) (ans *os.File, err error) { - bname := BytePtrFromString(name) - var fd uintptr - var errno unix.Errno - for { - fd, _, errno = unix.Syscall(unix.SYS_SHM_OPEN, uintptr(unsafe.Pointer(bname)), uintptr(flags), uintptr(perm)) - if errno != unix.EINTR { - if errno != 0 { - err = fmt.Errorf("shm_open() failed with error: %w", errno) - } - break - } - } - if err == nil { - ans = os.NewFile(fd, name) - } - return -} - -type syscall_based_mmap struct { - f *os.File - pos int64 - region []byte - unlinked bool -} - -func syscall_mmap(f *os.File, size uint64, access AccessFlags, truncate bool) (MMap, error) { - if truncate { - err := truncate_or_unlink(f, size, shm_unlink) - if err != nil { - return nil, fmt.Errorf("truncate failed with error: %w", err) - } - } - region, err := mmap(int(size), access, int(f.Fd()), 0) - if err != nil { - _ = f.Close() - _ = shm_unlink(f.Name()) - return nil, fmt.Errorf("mmap failed with error: %w", err) - } - return &syscall_based_mmap{f: f, region: region}, nil -} - -func (self *syscall_based_mmap) Name() string { - return self.f.Name() -} -func (self *syscall_based_mmap) Stat() (fs.FileInfo, error) { - return self.f.Stat() -} - -func (self *syscall_based_mmap) Flush() error { - return unix.Msync(self.region, unix.MS_SYNC) -} - -func (self *syscall_based_mmap) Slice() []byte { - return self.region -} - -func (self *syscall_based_mmap) Close() (err error) { - if self.region != nil { - self.f.Close() - munmap(self.region) - self.region = nil - } - return -} - -func (self *syscall_based_mmap) Unlink() (err error) { - if self.unlinked { - return nil - } - self.unlinked = true - return shm_unlink(self.Name()) -} - -func (self *syscall_based_mmap) Seek(offset int64, whence int) (ret int64, err error) { - switch whence { - case io.SeekStart: - self.pos = offset - case io.SeekEnd: - self.pos = int64(len(self.region)) + offset - case io.SeekCurrent: - self.pos += offset - } - return self.pos, nil -} - -func (self *syscall_based_mmap) Read(b []byte) (n int, err error) { - return Read(self, b) -} - -func (self *syscall_based_mmap) Write(b []byte) (n int, err error) { - return Write(self, b) -} - -func (self *syscall_based_mmap) IsFileSystemBacked() bool { return false } -func (self *syscall_based_mmap) FileSystemName() string { return "" } - -func create_temp(pattern string, size uint64) (ans MMap, err error) { - var prefix, suffix string - prefix, suffix, err = prefix_and_suffix(pattern) - if err != nil { - return - } - if SHM_REQUIRED_PREFIX != "" && !strings.HasPrefix(pattern, SHM_REQUIRED_PREFIX) { - // FreeBSD requires name to start with / - prefix = SHM_REQUIRED_PREFIX + prefix - } - var f *os.File - try := 0 - for { - name := prefix + utils.RandomFilename() + suffix - if len(name) > SHM_NAME_MAX { - return nil, ErrPatternTooLong - } - f, err = shm_open(name, os.O_EXCL|os.O_CREATE|os.O_RDWR, 0600) - if err != nil && (errors.Is(err, fs.ErrExist) || errors.Unwrap(err) == unix.EEXIST) { - try += 1 - if try > 10000 { - return nil, &os.PathError{Op: "createtemp", Path: prefix + "*" + suffix, Err: fs.ErrExist} - } - continue - } - break - } - if err != nil { - return nil, err - } - return syscall_mmap(f, size, WRITE, true) -} - -func Open(name string, size uint64) (MMap, error) { - ans, err := shm_open(name, os.O_RDONLY, 0) - if err != nil { - return nil, err - } - if size == 0 { - s, err := ans.Stat() - if err != nil { - ans.Close() - return nil, fmt.Errorf("Failed to stat SHM file with error: %w", err) - } - size = uint64(s.Size()) - } - return syscall_mmap(ans, size, READ, false) -} diff --git a/tools/utils/shm/shm_test.go b/tools/utils/shm/shm_test.go deleted file mode 100644 index 1cd2b7de8..000000000 --- a/tools/utils/shm/shm_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// License: GPLv3 Copyright: 2022, Kovid Goyal, - -package shm - -import ( - "crypto/rand" - "errors" - "fmt" - "io/fs" - "os" - "reflect" - "testing" -) - -var _ = fmt.Print - -func TestSHM(t *testing.T) { - data := make([]byte, 13347) - _, _ = rand.Read(data) - mm, err := CreateTemp("test-kitty-shm-", uint64(len(data))) - if err != nil { - t.Fatal(err) - } - - copy(mm.Slice(), data) - err = mm.Flush() - if err != nil { - t.Fatalf("Failed to msync() with error: %v", err) - } - err = mm.Close() - if err != nil { - t.Fatalf("Failed to close with error: %v", err) - } - - g, err := Open(mm.Name(), uint64(len(data))) - if err != nil { - t.Fatal(err) - } - data2 := g.Slice() - if !reflect.DeepEqual(data, data2) { - t.Fatalf("Could not read back written data: Written data length: %d Read data length: %d", len(data), len(data2)) - } - err = g.Close() - if err != nil { - t.Fatalf("Failed to close with error: %v", err) - } - err = g.Unlink() - if err != nil { - t.Fatalf("Failed to unlink with error: %v", err) - } - g, err = Open(mm.Name(), uint64(len(data))) - if err == nil { - t.Fatalf("Unlinking failed could re-open the SHM data. Data equal: %v Data length: %d", reflect.DeepEqual(g.Slice(), data), len(g.Slice())) - } - if mm.IsFileSystemBacked() { - _, err = os.Stat(mm.FileSystemName()) - if !errors.Is(err, fs.ErrNotExist) { - t.Fatalf("Unlinking %s did not work", mm.Name()) - } - } -} diff --git a/tools/utils/shm/specific_darwin.go b/tools/utils/shm/specific_darwin.go deleted file mode 100644 index 99222e952..000000000 --- a/tools/utils/shm/specific_darwin.go +++ /dev/null @@ -1,7 +0,0 @@ -// License: GPLv3 Copyright: 2022, Kovid Goyal, - -package shm - -const SHM_NAME_MAX = 30 -const SHM_REQUIRED_PREFIX = "" -const SHM_DIR = "" diff --git a/tools/utils/shm/specific_dragonfly.go b/tools/utils/shm/specific_dragonfly.go deleted file mode 100644 index 7e5bdcd36..000000000 --- a/tools/utils/shm/specific_dragonfly.go +++ /dev/null @@ -1,12 +0,0 @@ -// License: GPLv3 Copyright: 2022, Kovid Goyal, - -package shm - -import ( - "fmt" -) - -var _ = fmt.Print - -// https://www.dragonflybsd.org/cgi/web-man?command=shm_open§ion=3 -const SHM_DIR = "/var/run/shm" diff --git a/tools/utils/shm/specific_freebsd.go b/tools/utils/shm/specific_freebsd.go deleted file mode 100644 index b2c7b6b88..000000000 --- a/tools/utils/shm/specific_freebsd.go +++ /dev/null @@ -1,7 +0,0 @@ -// License: GPLv3 Copyright: 2022, Kovid Goyal, - -package shm - -const SHM_NAME_MAX = 1023 -const SHM_REQUIRED_PREFIX = "/" -const SHM_DIR = "" diff --git a/tools/utils/shm/specific_linux.go b/tools/utils/shm/specific_linux.go deleted file mode 100644 index cab8901e7..000000000 --- a/tools/utils/shm/specific_linux.go +++ /dev/null @@ -1,11 +0,0 @@ -// License: GPLv3 Copyright: 2022, Kovid Goyal, - -package shm - -import ( - "fmt" -) - -var _ = fmt.Print - -const SHM_DIR = "/dev/shm" diff --git a/tools/utils/shm/specific_netbsd.go b/tools/utils/shm/specific_netbsd.go deleted file mode 100644 index 209095a2e..000000000 --- a/tools/utils/shm/specific_netbsd.go +++ /dev/null @@ -1,11 +0,0 @@ -// License: GPLv3 Copyright: 2022, Kovid Goyal, - -package shm - -import ( - "fmt" -) - -var _ = fmt.Print - -const SHM_DIR = "/var/shm" diff --git a/tools/utils/shm/specific_openbsd.go b/tools/utils/shm/specific_openbsd.go deleted file mode 100644 index e1b604e62..000000000 --- a/tools/utils/shm/specific_openbsd.go +++ /dev/null @@ -1,11 +0,0 @@ -// License: GPLv3 Copyright: 2022, Kovid Goyal, - -package shm - -import ( - "fmt" -) - -var _ = fmt.Print - -const SHM_DIR = "/tmp"