Skip to content

Commit ba619f5

Browse files
committed
vmnet_test.go: Add TestVmnetNetworkShareModeSharingOverXpc
- Add `TestVmnetNetworkShareModeSharingOverXpc` to `vmnet_test.go` `TestVmnetNetworkShareModeSharingOverXpc` tests sharing `VmnetNetwork` in `SharedMode` over XPC communication. This test registers test executable as an Mach service and launches it using `launchctl`. The launched Mach service provides `VmnetNetwork` serialization to clients upon request, after booting a VM using the provided `VmnetNetwork` to ensure the network is functional on the server side. The client boots VM using the provided `VmnetNetwork` serialization. This test uses `pkg/xpc` package to implement XPC communication. - Add `pkg/xpc` package that providing `<xpc/xpc.h>` APIs to support implementing Mach service server and client Signed-off-by: Norio Nomura <[email protected]>
1 parent d5496fc commit ba619f5

File tree

9 files changed

+1438
-0
lines changed

9 files changed

+1438
-0
lines changed

pkg/xpc/error.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package xpc
2+
3+
/*
4+
#cgo darwin CFLAGS: -mmacosx-version-min=11 -x objective-c -fno-objc-arc
5+
# include "xpc.h"
6+
*/
7+
import "C"
8+
import (
9+
"runtime/cgo"
10+
"unsafe"
11+
)
12+
13+
// Error represents an XPC rich error.
14+
// see: https://developer.apple.com/documentation/xpc/xpc_rich_error_t?language=objc
15+
type Error = Object
16+
17+
var _ error = Error{}
18+
19+
// newError creates a new Error from an existing xpc_rich_error_t.
20+
// internal use only.
21+
func newError(richErr unsafe.Pointer) *Error {
22+
if richErr == nil {
23+
return nil
24+
}
25+
return NewObject(richErr)
26+
}
27+
28+
// wrapError wraps an existing xpc_rich_error_t into an Error and returns a handle.
29+
// intended to be called from C.
30+
//
31+
//export wrapError
32+
func wrapError(richErr unsafe.Pointer) uintptr {
33+
obj := newError(richErr)
34+
if obj == nil {
35+
return 0
36+
}
37+
obj.handle = cgo.NewHandle(obj)
38+
return uintptr(obj.handle)
39+
}
40+
41+
// unwrapError unwraps a handle into an *Error.
42+
func unwrapError(handle uintptr) *Error {
43+
if handle == 0 {
44+
return nil
45+
}
46+
return cgo.Handle(handle).Value().(*Error)
47+
}
48+
49+
// Error implements the error interface.
50+
func (e Error) Error() string {
51+
desc := C.xpcRichErrorCopyDescription(e.XpcObject)
52+
defer C.free(unsafe.Pointer(desc))
53+
return C.GoString(desc)
54+
}

pkg/xpc/listener.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package xpc
2+
3+
/*
4+
#cgo darwin CFLAGS: -mmacosx-version-min=11 -x objective-c -fno-objc-arc
5+
#cgo darwin LDFLAGS: -lobjc -framework Foundation
6+
# include "xpc.h"
7+
*/
8+
import "C"
9+
import (
10+
"runtime/cgo"
11+
"unsafe"
12+
)
13+
14+
// Listener represents an XPC listener.
15+
type Listener struct {
16+
// Exported for use in other packages since unimplemented XPC API may require direct access to xpc_listener_t.
17+
XpcListener unsafe.Pointer
18+
queue unsafe.Pointer
19+
sessionHandler cgo.Handle
20+
}
21+
22+
// SessionHandler is a function that handles incoming sessions.
23+
type SessionHandler func(session *Session)
24+
25+
type listenerOption struct {
26+
sessionHandler SessionHandler
27+
messageHandler MessageHandler
28+
cancellationHandler CancellationHandler
29+
peerRequirement *PeerRequirement
30+
}
31+
32+
// NewListener creates a new XPC listener for the given service name.
33+
// You need to call [Listener.Activate] to start accepting incoming connections.
34+
func NewListener(service string, handler SessionHandler) (*Listener, error) {
35+
cname := C.CString(service)
36+
defer C.free(unsafe.Pointer(cname))
37+
// Use a serial dispatch queue for the listener,
38+
// because the vmnet framework API does not seem to work well with concurrent queues.
39+
// For example, vmnet_network_create fails when using a concurrent queue.
40+
queue := C.dispatchQueueCreateSerial(cname)
41+
cgoSessionHandler := cgo.NewHandle(handler)
42+
43+
var err_out unsafe.Pointer
44+
ptr := C.xpcListenerCreate(
45+
cname,
46+
queue,
47+
C.XPC_LISTENER_CREATE_INACTIVE,
48+
C.uintptr_t(cgoSessionHandler),
49+
&err_out,
50+
)
51+
if err_out != nil {
52+
C.dispatchRelease(queue)
53+
cgoSessionHandler.Delete()
54+
return nil, newError(err_out)
55+
}
56+
return &Listener{
57+
XpcListener: ptr,
58+
queue: queue,
59+
sessionHandler: cgoSessionHandler,
60+
}, nil
61+
}
62+
63+
// Activate starts the listener to accept incoming connections.
64+
// see: https://developer.apple.com/documentation/xpc/xpc_listener_activate
65+
func (l *Listener) Activate() error {
66+
var err_out unsafe.Pointer
67+
C.xpcListenerActivate(l.XpcListener, &err_out)
68+
if err_out != nil {
69+
return newError(err_out)
70+
}
71+
return nil
72+
}
73+
74+
// Close stops the listener from accepting incoming connections.
75+
// see: https://developer.apple.com/documentation/xpc/xpc_listener_cancel
76+
func (l *Listener) Close() error {
77+
C.xpcListenerCancel(l.XpcListener)
78+
79+
C.xpcRelease(l.XpcListener)
80+
if l.sessionHandler != 0 {
81+
l.sessionHandler.Delete()
82+
l.sessionHandler = 0
83+
}
84+
C.dispatchRelease(l.queue)
85+
return nil
86+
}
87+
88+
// SetPeerRequirement sets the peer requirement for the listener.
89+
// see: https://developer.apple.com/documentation/xpc/xpc_listener_set_peer_requirement
90+
func (l *Listener) SetPeerRequirement(req *PeerRequirement) {
91+
C.xpcListenerSetPeerRequirement(l.XpcListener, req.XpcObject)
92+
}
93+
94+
// String returns a description of the listener.
95+
// see: https://developer.apple.com/documentation/xpc/xpc_listener_copy_description
96+
func (l *Listener) String() string {
97+
desc := C.xpcListenerCopyDescription(l.XpcListener)
98+
defer C.free(unsafe.Pointer(desc))
99+
return C.GoString(desc)
100+
}

pkg/xpc/object.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package xpc
2+
3+
/*
4+
#cgo darwin CFLAGS: -mmacosx-version-min=11 -x objective-c -fno-objc-arc
5+
# include "xpc.h"
6+
*/
7+
import "C"
8+
import (
9+
"runtime"
10+
"runtime/cgo"
11+
"unsafe"
12+
)
13+
14+
// Object represents a generic XPC object.
15+
// see: https://developer.apple.com/documentation/xpc/xpc_object_t?language=objc
16+
type Object struct {
17+
// Exported for use in other packages since unimplemented XPC API may require direct access to xpc_object_t.
18+
XpcObject unsafe.Pointer
19+
handle cgo.Handle
20+
}
21+
22+
// NewObject creates a new Object from an existing xpc_object_t.
23+
func NewObject(xpcObj unsafe.Pointer) *Object {
24+
if xpcObj == nil {
25+
return nil
26+
}
27+
return &Object{XpcObject: xpcObj}
28+
}
29+
30+
// wrapObject wraps an existing xpc_object_t into an Object and returns a handle.
31+
// intended to be called from C.
32+
//
33+
//export wrapObject
34+
func wrapObject(o unsafe.Pointer) uintptr {
35+
obj := NewObject(o)
36+
obj.handle = cgo.NewHandle(obj)
37+
return uintptr(obj.handle)
38+
}
39+
40+
// unwrapObject unwraps a handle into an *Object.
41+
func unwrapObject(handle uintptr) *Object {
42+
if handle == 0 {
43+
return nil
44+
}
45+
return cgo.Handle(handle).Value().(*Object)
46+
}
47+
48+
// NewDictionary creates a new empty XPC dictionary object.
49+
// You can customize the dictionary object using DictionaryEntry.
50+
// see: https://developer.apple.com/documentation/xpc/xpc_dictionary_create_empty()?language=objc
51+
func NewDictionary(entries ...DictionaryEntry) *Object {
52+
obj := NewObject(C.xpcDictionaryCreateEmpty())
53+
for _, e := range entries {
54+
e(obj)
55+
}
56+
return obj
57+
}
58+
59+
// Retain retains the XPC object and returns itself.
60+
// see: https://developer.apple.com/documentation/xpc/xpc_retain?language=objc
61+
func (o *Object) Retain() *Object {
62+
C.xpcRetain(o.XpcObject)
63+
runtime.SetFinalizer(o, func(o *Object) {
64+
o.release()
65+
})
66+
return o
67+
}
68+
69+
// release releases the XPC object.
70+
// see: https://developer.apple.com/documentation/xpc/xpc_release?language=objc
71+
func (o *Object) release() {
72+
if o.handle != 0 {
73+
o.handle.Delete()
74+
o.handle = 0
75+
}
76+
C.xpcRelease(o.XpcObject)
77+
}
78+
79+
// DictionaryGetString retrieves a string value from the XPC dictionary object by key.
80+
// Returns an empty string if the key does not exist.
81+
// see: https://developer.apple.com/documentation/xpc/xpc_dictionary_get_string(_:_:)?language=objc
82+
func (o *Object) DictionaryGetString(key string) string {
83+
cKey := C.CString(key)
84+
defer C.free(unsafe.Pointer(cKey))
85+
val := C.xpcDictionaryGetString(o.XpcObject, cKey)
86+
return C.GoString(val)
87+
}
88+
89+
// DictionaryGetValue retrieves a value from the XPC dictionary object by key.
90+
// Returns nil if the key does not exist.
91+
// see: https://developer.apple.com/documentation/xpc/xpc_dictionary_get_value(_:_:)?language=objc
92+
func (o *Object) DictionaryGetValue(key string) *Object {
93+
cKey := C.CString(key)
94+
defer C.free(unsafe.Pointer(cKey))
95+
val := C.xpcDictionaryGetValue(o.XpcObject, cKey)
96+
if val == nil {
97+
return nil
98+
}
99+
return NewObject(val)
100+
}
101+
102+
// DictionaryEntry defines a function type for customizing reply objects.
103+
type DictionaryEntry func(*Object)
104+
105+
// WithString sets a string value in the reply dictionary.
106+
// see: https://developer.apple.com/documentation/xpc/xpc_dictionary_set_string(_:_:_:)?language=objc
107+
func WithString(key, value string) DictionaryEntry {
108+
return func(o *Object) {
109+
cKey := C.CString(key)
110+
defer C.free(unsafe.Pointer(cKey))
111+
cValue := C.CString(value)
112+
defer C.free(unsafe.Pointer(cValue))
113+
C.xpcDictionarySetString(o.XpcObject, cKey, cValue)
114+
}
115+
}
116+
117+
// WithValue sets a value in the reply dictionary.
118+
// see: https://developer.apple.com/documentation/xpc/xpc_dictionary_set_value(_:_:_:)?language=objc
119+
func WithValue(key string, val *Object) DictionaryEntry {
120+
return func(o *Object) {
121+
cKey := C.CString(key)
122+
defer C.free(unsafe.Pointer(cKey))
123+
C.xpcDictionarySetValue(o.XpcObject, cKey, val.XpcObject)
124+
}
125+
}
126+
127+
// DictionaryCreateReply creates a new reply XPC dictionary object based on the current object.
128+
// see: https://developer.apple.com/documentation/xpc/xpc_dictionary_create_reply(_:)?language=objc
129+
// You can customize the reply dictionary using DictionaryEntry.
130+
func (o *Object) DictionaryCreateReply(entries ...DictionaryEntry) *Object {
131+
reply := C.xpcDictionaryCreateReply(o.XpcObject)
132+
obj := NewObject(unsafe.Pointer(reply))
133+
for _, entry := range entries {
134+
entry(obj)
135+
}
136+
return obj
137+
}
138+
139+
// String returns the description of the XPC object.
140+
// see: https://developer.apple.com/documentation/xpc/xpc_copy_description(_:)?language=objc
141+
func (o *Object) String() string {
142+
cs := C.xpcCopyDescription(o.XpcObject)
143+
defer C.free(unsafe.Pointer(cs))
144+
return C.GoString(cs)
145+
}

pkg/xpc/peer_requirement.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package xpc
2+
3+
/*
4+
#cgo darwin CFLAGS: -mmacosx-version-min=11 -x objective-c -fno-objc-arc
5+
#cgo darwin LDFLAGS: -lobjc -framework Foundation
6+
# include "xpc.h"
7+
*/
8+
import "C"
9+
import "unsafe"
10+
11+
// PeerRequirement represents an xpc_peer_requirement_t.
12+
// see: https://developer.apple.com/documentation/xpc/xpc_peer_requirement_t?language=objc
13+
type PeerRequirement struct {
14+
Object
15+
}
16+
17+
// CreatePeerRequirementLwcr creates a peer requirement from a LWCR object.
18+
// see: https://developer.apple.com/documentation/xpc/xpc_peer_requirement_create_lwcr
19+
// see: https://developer.apple.com/documentation/security/defining-launch-environment-and-library-constraints?language=objc
20+
func CreatePeerRequirementLwcr(lwcr *Object) (*PeerRequirement, error) {
21+
var err_out unsafe.Pointer
22+
ptr := C.xpcPeerRequirementCreateLwcr(
23+
lwcr.XpcObject,
24+
&err_out,
25+
)
26+
if err_out != nil {
27+
return nil, newError(err_out)
28+
}
29+
return &PeerRequirement{
30+
Object: Object{XpcObject: ptr},
31+
}, nil
32+
}

0 commit comments

Comments
 (0)