Skip to content

Commit 9506cbd

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 9506cbd

File tree

9 files changed

+1030
-0
lines changed

9 files changed

+1030
-0
lines changed

pkg/xpc/error.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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 struct {
16+
Object
17+
}
18+
19+
var _ error = Error{}
20+
21+
// newError creates a new Error from an existing xpc_rich_error_t.
22+
// internal use only.
23+
func newError(richErr unsafe.Pointer) *Error {
24+
return &Error{
25+
Object: Object{XpcObject: richErr},
26+
}
27+
}
28+
29+
// wrapError wraps an existing xpc_rich_error_t into an Error and returns a handle.
30+
// intended to be called from C.
31+
//
32+
//export wrapError
33+
func wrapError(richErr unsafe.Pointer) uintptr {
34+
obj := newError(richErr)
35+
obj.handle = cgo.NewHandle(obj)
36+
return uintptr(obj.handle)
37+
}
38+
39+
// unwrapError unwraps a handle into an *Error.
40+
func unwrapError(handle uintptr) *Error {
41+
return cgo.Handle(handle).Value().(*Error)
42+
}
43+
44+
// Error implements the error interface.
45+
func (e Error) Error() string {
46+
desc := C.xpcRichErrorCopyDescription(e.XpcObject)
47+
defer C.free(unsafe.Pointer(desc))
48+
return C.GoString(desc)
49+
}

pkg/xpc/listener.go

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

pkg/xpc/object.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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+
// Object represents a generic XPC object.
14+
// see: https://developer.apple.com/documentation/xpc/xpc_object_t?language=objc
15+
type Object struct {
16+
// Exported for use in other packages since unimplemented XPC API may require direct access to xpc_object_t.
17+
XpcObject unsafe.Pointer
18+
handle cgo.Handle
19+
}
20+
21+
// NewObject creates a new Object from an existing xpc_object_t.
22+
func NewObject(xpcObj unsafe.Pointer) *Object {
23+
return &Object{XpcObject: xpcObj}
24+
}
25+
26+
// wrapObject wraps an existing xpc_object_t into an Object and returns a handle.
27+
// intended to be called from C.
28+
//
29+
//export wrapObject
30+
func wrapObject(o unsafe.Pointer) uintptr {
31+
obj := &Object{XpcObject: o}
32+
obj.handle = cgo.NewHandle(obj)
33+
return uintptr(obj.handle)
34+
}
35+
36+
// unwrapObject unwraps a handle into an *Object.
37+
func unwrapObject(handle uintptr) *Object {
38+
return cgo.Handle(handle).Value().(*Object)
39+
}
40+
41+
// NewDictionary creates a new empty XPC dictionary object.
42+
// You can customize the dictionary object using DictionaryEntry.
43+
func NewDictionary(entries ...DictionaryEntry) *Object {
44+
obj := &Object{XpcObject: C.xpcDictionaryCreateEmpty()}
45+
for _, e := range entries {
46+
e(obj)
47+
}
48+
return obj
49+
}
50+
51+
// Retain retains the XPC object and returns itself.
52+
// see: https://developer.apple.com/documentation/xpc/xpc_retain?language=objc
53+
func (o *Object) Retain() *Object {
54+
C.xpcRetain(o.XpcObject)
55+
return o
56+
}
57+
58+
// Release releases the XPC object.
59+
// see: https://developer.apple.com/documentation/xpc/xpc_release?language=objc
60+
func (o *Object) Release() {
61+
if o.handle != 0 {
62+
o.handle.Delete()
63+
o.handle = 0
64+
}
65+
C.xpcRelease(o.XpcObject)
66+
}
67+
68+
// DisctionaryGetString retrieves a string value from the XPC dictionary object by key.
69+
// Returns an empty string if the key does not exist.
70+
// see: https://developer.apple.com/documentation/xpc/xpc_dictionary_get_string(_:_:)?language=objc
71+
func (o *Object) DisctionaryGetString(key string) string {
72+
cKey := C.CString(key)
73+
defer C.free(unsafe.Pointer(cKey))
74+
val := C.xpcDictionaryGetString(o.XpcObject, cKey)
75+
return C.GoString(val)
76+
}
77+
78+
// DisctionaryGetValue retrieves a value from the XPC dictionary object by key.
79+
// Returns nil if the key does not exist.
80+
// see: https://developer.apple.com/documentation/xpc/xpc_dictionary_get_value(_:_:)?language=objc
81+
func (o *Object) DisctionaryGetValue(key string) *Object {
82+
cKey := C.CString(key)
83+
defer C.free(unsafe.Pointer(cKey))
84+
val := C.xpcDictionaryGetValue(o.XpcObject, cKey)
85+
if val == nil {
86+
return nil
87+
}
88+
return NewObject(val)
89+
}
90+
91+
// DictionaryEntry defines a function type for customizing reply objects.
92+
type DictionaryEntry func(*Object)
93+
94+
// WithString sets a string value in the reply dictionary.
95+
func WithString(key, value string) DictionaryEntry {
96+
return func(o *Object) {
97+
cKey := C.CString(key)
98+
defer C.free(unsafe.Pointer(cKey))
99+
cValue := C.CString(value)
100+
defer C.free(unsafe.Pointer(cValue))
101+
C.xpcDictionarySetString(o.XpcObject, cKey, cValue)
102+
}
103+
}
104+
105+
// WithValue sets a value in the reply dictionary.
106+
func WithValue(key string, val *Object) DictionaryEntry {
107+
return func(o *Object) {
108+
cKey := C.CString(key)
109+
defer C.free(unsafe.Pointer(cKey))
110+
C.xpcDictionarySetValue(o.XpcObject, cKey, val.XpcObject)
111+
}
112+
}
113+
114+
// DisctionaryCreateReply creates a new reply XPC dictionary object based on the current object.
115+
// see: https://developer.apple.com/documentation/xpc/xpc_dictionary_create_reply(_:)?language=objc
116+
// You can customize the reply object using ReplyOption.
117+
func (o *Object) DisctionaryCreateReply(entries ...DictionaryEntry) *Object {
118+
reply := C.xpcDictionaryCreateReply(o.XpcObject)
119+
obj := NewObject(unsafe.Pointer(reply))
120+
for _, entry := range entries {
121+
entry(obj)
122+
}
123+
return obj
124+
}

pkg/xpc/peer_requirement.go

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

0 commit comments

Comments
 (0)