aboutsummaryrefslogtreecommitdiffhomepage
path: root/cmd/fshim/ipc/wayland.go
blob: 132e74f42ad0cb400f19eb446087b452d02dab40 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package shim0

import (
	"fmt"
	"net"
	"sync"
	"syscall"

	"git.ophivana.moe/security/fortify/internal/fmsg"
)

// Wayland implements wayland mediation.
type Wayland struct {
	// wayland socket path
	Path string

	// wayland connection
	conn *net.UnixConn

	connErr error
	sync.Once
	// wait for wayland client to exit
	done chan struct{}
}

func (wl *Wayland) WriteUnix(conn *net.UnixConn) error {
	// connect to host wayland socket
	if f, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: wl.Path, Net: "unix"}); err != nil {
		return fmsg.WrapErrorSuffix(err,
			fmt.Sprintf("cannot connect to wayland at %q:", wl.Path))
	} else {
		fmsg.VPrintf("connected to wayland at %q", wl.Path)
		wl.conn = f
	}

	// set up for passing wayland socket
	if rc, err := wl.conn.SyscallConn(); err != nil {
		return fmsg.WrapErrorSuffix(err, "cannot obtain raw wayland connection:")
	} else {
		ec := make(chan error)
		go func() {
			// pass wayland connection fd
			if err = rc.Control(func(fd uintptr) {
				if _, _, err = conn.WriteMsgUnix(nil, syscall.UnixRights(int(fd)), nil); err != nil {
					ec <- fmsg.WrapErrorSuffix(err, "cannot pass wayland connection to shim:")
					return
				}
				ec <- nil

				// block until shim exits
				<-wl.done
				fmsg.VPrintln("releasing wayland connection")
			}); err != nil {
				ec <- fmsg.WrapErrorSuffix(err, "cannot obtain wayland connection fd:")
				return
			}
		}()
		return <-ec
	}
}

func (wl *Wayland) Close() error {
	wl.Do(func() {
		close(wl.done)
		wl.connErr = wl.conn.Close()
	})

	return wl.connErr
}

func NewWayland() *Wayland {
	wl := new(Wayland)
	wl.done = make(chan struct{})
	return wl
}