Skip to content

Commit d3fad75

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 30e3f7e commit d3fad75

File tree

14 files changed

+2110
-1
lines changed

14 files changed

+2110
-1
lines changed

pkg/xpc/array.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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+
"iter"
10+
"runtime/cgo"
11+
"unsafe"
12+
)
13+
14+
// Array represents an XPC array([XPC_TYPE_ARRAY]) object. [TypeArray]
15+
//
16+
// [XPC_TYPE_ARRAY]: https://developer.apple.com/documentation/xpc/xpc_type_array-c.macro?language=objc
17+
type Array struct {
18+
*XpcObject
19+
}
20+
21+
var _ Object = &Array{}
22+
23+
// NewArray creates a new [Array] from the given [Object]s.
24+
//
25+
// - https://developer.apple.com/documentation/xpc/xpc_array_create(_:_:)?language=objc
26+
func NewArray(objects ...Object) *Array {
27+
cObjects := make([]unsafe.Pointer, len(objects))
28+
for i, obj := range objects {
29+
cObjects[i] = obj.Raw()
30+
}
31+
xpcObject := &XpcObject{C.xpcArrayCreate(
32+
(*unsafe.Pointer)(unsafe.Pointer(&cObjects[0])),
33+
C.size_t(len(cObjects)),
34+
)}
35+
a := &Array{XpcObject: xpcObject}
36+
return a
37+
}
38+
39+
// Count returns the number of elements in the [Array].
40+
//
41+
// - https://developer.apple.com/documentation/xpc/xpc_array_get_count(_:)?language=objc
42+
func (a *Array) Count() int {
43+
return int(C.xpcArrayGetCount(a.Raw()))
44+
}
45+
46+
// ArrayApplier is a function type for applying to each element in the Array.
47+
type ArrayApplier func(uint64, Object) bool
48+
49+
// callArrayApplier is called from C to apply a function to each element in the Array.
50+
//
51+
//export callArrayApplier
52+
func callArrayApplier(cgoApplier uintptr, index C.size_t, cgoValue uintptr) C.bool {
53+
applier := unwrapHandler[ArrayApplier](cgoApplier)
54+
value := unwrapObject[Object](cgoValue)
55+
result := applier(uint64(index), value)
56+
return C.bool(result)
57+
}
58+
59+
// All iterates over all elements in the [Array].
60+
//
61+
// - https://developer.apple.com/documentation/xpc/xpc_array_apply(_:_:)?language=objc
62+
func (a *Array) All() iter.Seq2[uint64, Object] {
63+
return func(yieald func(uint64, Object) bool) {
64+
cgoApplier := cgo.NewHandle(ArrayApplier(yieald))
65+
defer cgoApplier.Delete()
66+
_ = C.xpcArrayApply(
67+
a.Raw(),
68+
C.uintptr_t(cgoApplier),
69+
)
70+
}
71+
}
72+
73+
// Values iterates over all values in the [Array] using [Array.All].
74+
func (a *Array) Values() iter.Seq[Object] {
75+
return func(yieald func(Object) bool) {
76+
for _, value := range a.All() {
77+
if !yieald(value) {
78+
return
79+
}
80+
}
81+
}
82+
}

pkg/xpc/cgo_handle.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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 "runtime/cgo"
9+
10+
// cgoHandler holds a cgo.Handle for an Object.
11+
// It provides methods to hold and release the handle.
12+
// handle will released when cgoHandler.release is called.
13+
type cgoHandler struct {
14+
handle cgo.Handle
15+
}
16+
17+
// release releases the cgo.Handle.
18+
func (h *cgoHandler) release() {
19+
if h.handle != 0 {
20+
h.handle.Delete()
21+
h.handle = 0
22+
}
23+
}
24+
25+
// newCgoHandler creates a new cgoHandler and holds the given value.
26+
func newCgoHandler(v any) (*cgoHandler, C.uintptr_t) {
27+
if v == nil {
28+
return nil, 0
29+
}
30+
h := &cgoHandler{cgo.NewHandle(v)}
31+
return ReleaseInFinalizer(h), C.uintptr_t(h.handle)
32+
}
33+
34+
// unwrapHandler unwraps the cgo.Handle from the given uintptr and returns the associated value.
35+
// It does NOT delete the handle; it expects the handle to be managed by cgoHandler or caller.
36+
func unwrapHandler[T any](handle uintptr) T {
37+
if handle == 0 {
38+
var zero T
39+
return zero
40+
}
41+
return cgo.Handle(handle).Value().(T)
42+
}

pkg/xpc/dictionary.go

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
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+
"fmt"
10+
"iter"
11+
"runtime/cgo"
12+
"unsafe"
13+
)
14+
15+
// Dictionary represents an XPC dictionary (XPC_TYPE_DICTIONARY) object. [TypeDictionary]
16+
// - https://developer.apple.com/documentation/xpc/xpc_type_dictionary-c.macro?language=objc
17+
type Dictionary struct {
18+
*XpcObject
19+
}
20+
21+
var _ Object = &Dictionary{}
22+
23+
// NewDictionary creates a new empty [Dictionary] object and applies the given entries.
24+
// - https://developer.apple.com/documentation/xpc/xpc_dictionary_create_empty()?language=objc
25+
//
26+
// The entries can be created using [DictionaryEntry] functions such as [KeyValue].
27+
func NewDictionary(entries ...DictionaryEntry) *Dictionary {
28+
d := &Dictionary{XpcObject: &XpcObject{C.xpcDictionaryCreateEmpty()}}
29+
for _, e := range entries {
30+
e(d)
31+
}
32+
return d
33+
}
34+
35+
// DictionaryEntry defines a function type for customizing [NewDictionary] or [Dictionary.CreateReply].
36+
type DictionaryEntry func(*Dictionary)
37+
38+
// KeyValue sets a [Object] value for the given key in the [Dictionary].
39+
// - https://developer.apple.com/documentation/xpc/xpc_dictionary_set_value(_:_:_:)?language=objc
40+
func KeyValue(key string, val Object) DictionaryEntry {
41+
return func(o *Dictionary) {
42+
o.SetValue(key, val)
43+
}
44+
}
45+
46+
// DictionaryApplier is a function type for applying to each key-value pair in the XPC dictionary.
47+
type DictionaryApplier func(string, Object) bool
48+
49+
// callDictionaryApplier is called from C to apply a function to each key-value pair in the XPC dictionary object.
50+
//
51+
//export callDictionaryApplier
52+
func callDictionaryApplier(cgoApplier uintptr, cKey *C.char, cgoValue uintptr) C.bool {
53+
applier := unwrapHandler[DictionaryApplier](cgoApplier)
54+
return C.bool(applier(C.GoString(cKey), unwrapObject[Object](cgoValue)))
55+
}
56+
57+
// All iterates over all key-value pairs in the [Dictionary].
58+
// - https://developer.apple.com/documentation/xpc/xpc_dictionary_apply(_:_:)?language=objc
59+
func (o *Dictionary) All() iter.Seq2[string, Object] {
60+
return func(yieald func(string, Object) bool) {
61+
cgoApplier := cgo.NewHandle(DictionaryApplier(yieald))
62+
defer cgoApplier.Delete()
63+
C.xpcDictionaryApply(o.Raw(), C.uintptr_t(cgoApplier))
64+
}
65+
}
66+
67+
// Keys iterates over all keys in the [Dictionary] using [Dictionary.All].
68+
func (o *Dictionary) Keys() iter.Seq[string] {
69+
return func(yieald func(string) bool) {
70+
for key := range o.All() {
71+
if !yieald(key) {
72+
return
73+
}
74+
}
75+
}
76+
}
77+
78+
// Values iterates over all [Object] values in the [Dictionary] using [Dictionary.All].
79+
func (o *Dictionary) Values() iter.Seq[Object] {
80+
return func(yieald func(Object) bool) {
81+
for _, value := range o.All() {
82+
if !yieald(value) {
83+
return
84+
}
85+
}
86+
}
87+
}
88+
89+
// Entries iterates over all [DictionaryEntry] entries in the [Dictionary] using [Dictionary.All].
90+
func (o *Dictionary) Entries() iter.Seq[DictionaryEntry] {
91+
return func(yieald func(DictionaryEntry) bool) {
92+
for key, value := range o.All() {
93+
if !yieald(KeyValue(key, value)) {
94+
return
95+
}
96+
}
97+
}
98+
}
99+
100+
// GetData retrieves a byte slice value from the [Dictionary] by key.
101+
// - https://developer.apple.com/documentation/xpc/xpc_dictionary_get_data(_:_:_:)?language=objc
102+
//
103+
// Returns nil if the key does not exist.
104+
func (o *Dictionary) GetData(key string) []byte {
105+
cKey := C.CString(key)
106+
defer C.free(unsafe.Pointer(cKey))
107+
var n C.size_t
108+
p := C.xpcDictionaryGetData(o.Raw(), cKey, &n)
109+
if p == nil || n == 0 {
110+
return nil
111+
}
112+
return C.GoBytes(p, C.int(n))
113+
}
114+
115+
// GetString retrieves a string value from the [Dictionary] by key.
116+
// - https://developer.apple.com/documentation/xpc/xpc_dictionary_get_string(_:_:)?language=objc
117+
//
118+
// Returns an empty string if the key does not exist.
119+
func (o *Dictionary) GetString(key string) string {
120+
cKey := C.CString(key)
121+
defer C.free(unsafe.Pointer(cKey))
122+
val := C.xpcDictionaryGetString(o.Raw(), cKey)
123+
return C.GoString(val)
124+
}
125+
126+
// SetValue sets an [Object] value for the given key in the [Dictionary].
127+
// - https://developer.apple.com/documentation/xpc/xpc_dictionary_set_value(_:_:_:)?language=objc
128+
func (o *Dictionary) SetValue(key string, val Object) {
129+
cKey := C.CString(key)
130+
defer C.free(unsafe.Pointer(cKey))
131+
C.xpcDictionarySetValue(o.Raw(), cKey, val.Raw())
132+
}
133+
134+
// GetValue retrieves an [Object] value from the [Dictionary] by key.
135+
// - https://developer.apple.com/documentation/xpc/xpc_dictionary_get_value(_:_:)?language=objc
136+
//
137+
// Returns nil if the key does not exist.
138+
func (o *Dictionary) GetValue(key string) Object {
139+
cKey := C.CString(key)
140+
defer C.free(unsafe.Pointer(cKey))
141+
val := C.xpcDictionaryGetValue(o.Raw(), cKey)
142+
if val == nil {
143+
return nil
144+
}
145+
return NewObject(val)
146+
}
147+
148+
// DictionaryCreateReply creates a new reply [Dictionary] based on the current [Dictionary].
149+
// - https://developer.apple.com/documentation/xpc/xpc_dictionary_create_reply(_:)?language=objc
150+
//
151+
// The entries can be created using [DictionaryEntry] functions such as [KeyValue].
152+
func (o *Dictionary) CreateReply(entries ...DictionaryEntry) *Dictionary {
153+
d := &Dictionary{XpcObject: &XpcObject{C.xpcDictionaryCreateReply(o.Raw())}}
154+
for _, entry := range entries {
155+
entry(d)
156+
}
157+
return d
158+
}
159+
160+
// SenderSatisfies checks if the sender of the message [Dictionary] satisfies the given [PeerRequirement].
161+
// - https://developer.apple.com/documentation/xpc/xpc_peer_requirement_match_received_message?language=objc
162+
func (d *Dictionary) SenderSatisfies(requirement *PeerRequirement) (bool, error) {
163+
var err_out unsafe.Pointer
164+
res := C.xpcPeerRequirementMatchReceivedMessage(requirement.Raw(), d.Raw(), &err_out)
165+
if err_out != nil {
166+
return false, fmt.Errorf("error matching peer requirement: %w", newRichError(err_out))
167+
}
168+
return bool(res), nil
169+
}

pkg/xpc/error.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
"unsafe"
10+
)
11+
12+
// RichError represents an XPC rich error. ([XPC_TYPE_RICH_ERROR]) [TypeRichError]
13+
//
14+
// [XPC_TYPE_RICH_ERROR]: https://developer.apple.com/documentation/xpc/xpc_rich_error_t?language=objc
15+
type RichError struct {
16+
*XpcObject
17+
}
18+
19+
var _ Object = &RichError{}
20+
21+
var _ error = RichError{}
22+
23+
// newRichError creates a new RichError from an existing xpc_rich_error_t.
24+
// internal use only.
25+
func newRichError(richErr unsafe.Pointer) *RichError {
26+
if richErr == nil {
27+
return nil
28+
}
29+
return &RichError{XpcObject: &XpcObject{richErr}}
30+
}
31+
32+
// CanRetry indicates whether the operation that caused the [RichError] can be retried.
33+
//
34+
// - https://developer.apple.com/documentation/xpc/xpc_rich_error_can_retry(_:)?language=objc
35+
func (e RichError) CanRetry() bool {
36+
return bool(C.xpcRichErrorCanRetry(e.Raw()))
37+
}
38+
39+
// Error implements the [error] interface.
40+
//
41+
// - https://developer.apple.com/documentation/xpc/xpc_rich_error_copy_description(_:)?language=objc
42+
func (e RichError) Error() string {
43+
desc := C.xpcRichErrorCopyDescription(e.Raw())
44+
defer C.free(unsafe.Pointer(desc))
45+
return C.GoString(desc)
46+
}

0 commit comments

Comments
 (0)