Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added startWithOptionsCompletionHandler #92

Merged
merged 1 commit into from
Oct 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion issues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func TestIssue50(t *testing.T) {

t.Run("check for segmentation faults", func(t *testing.T) {
cases := map[string]func() error{
"start handler": m.Start,
"start handler": func() error { return m.Start() },
"pause handler": m.Pause,
"resume handler": m.Resume,
"stop handler": m.Stop,
Expand Down
42 changes: 29 additions & 13 deletions virtualization.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package vz
#cgo darwin CFLAGS: -x objective-c -fno-objc-arc
#cgo darwin LDFLAGS: -lobjc -framework Foundation -framework Virtualization -framework Cocoa
# include "virtualization.h"
# include "virtualization_13.h"
*/
import "C"
import (
Expand Down Expand Up @@ -240,26 +241,47 @@ func makeHandler() (func(error), chan error) {
}, ch
}

type virtualMachineStartOptions struct {
macOSVirtualMachineStartOptionsPtr unsafe.Pointer
}

// VirtualMachineStartOption is an option for virtual machine start.
type VirtualMachineStartOption func(*virtualMachineStartOptions) error

// Start a virtual machine that is in either Stopped or Error state.
//
// If you want to listen status change events, use the "StateChangedNotify" method.
//
// This is only supported on macOS 11 and newer, on older versions fn will be called immediately
// with ErrUnsupportedOSVersion.
func (v *VirtualMachine) Start() error {
// If options are specified, also checks whether these options are
// available in use your macOS version available.
func (v *VirtualMachine) Start(opts ...VirtualMachineStartOption) error {
o := &virtualMachineStartOptions{}
for _, optFunc := range opts {
if err := optFunc(o); err != nil {
return err
}
}

h, errCh := makeHandler()
handler := cgo.NewHandle(h)
defer handler.Delete()
C.startWithCompletionHandler(v.Ptr(), v.dispatchQueue, unsafe.Pointer(&handler))

if o.macOSVirtualMachineStartOptionsPtr != nil {
C.startWithOptionsCompletionHandler(
v.Ptr(),
v.dispatchQueue,
o.macOSVirtualMachineStartOptionsPtr,
unsafe.Pointer(&handler),
)
} else {
C.startWithCompletionHandler(v.Ptr(), v.dispatchQueue, unsafe.Pointer(&handler))
}
return <-errCh
}

// Pause a virtual machine that is in Running state.
//
// If you want to listen status change events, use the "StateChangedNotify" method.
//
// This is only supported on macOS 11 and newer, on older versions fn will be called immediately
// with ErrUnsupportedOSVersion.
func (v *VirtualMachine) Pause() error {
h, errCh := makeHandler()
handler := cgo.NewHandle(h)
Expand All @@ -271,9 +293,6 @@ func (v *VirtualMachine) Pause() error {
// Resume a virtual machine that is in the Paused state.
//
// If you want to listen status change events, use the "StateChangedNotify" method.
//
// This is only supported on macOS 11 and newer, on older versions fn will be called immediately
// with ErrUnsupportedOSVersion.
func (v *VirtualMachine) Resume() error {
h, errCh := makeHandler()
handler := cgo.NewHandle(h)
Expand All @@ -286,9 +305,6 @@ func (v *VirtualMachine) Resume() error {
//
// If returned error is not nil, assigned with the error if the request failed.
// Returns true if the request was made successfully.
//
// This is only supported on macOS 11 and newer, ErrUnsupportedOSVersion will
// be returned on older versions.
func (v *VirtualMachine) RequestStop() (bool, error) {
nserr := newNSErrorAsNil()
nserrPtr := nserr.Ptr()
Expand Down
3 changes: 1 addition & 2 deletions virtualization.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@

#pragma once

#import <Foundation/Foundation.h>
#import "virtualization_helper.h"
#import <Virtualization/Virtualization.h>

/* exported from cgo */
void virtualMachineCompletionHandler(void *cgoHandler, void *errPtr);
void connectionHandler(void *connection, void *err, void *cgoHandlerPtr);
void changeStateOnObserver(int state, void *cgoHandler);
bool shouldAcceptNewConnectionHandler(void *cgoHandler, void *connection, void *socketDevice);
Expand Down
10 changes: 0 additions & 10 deletions virtualization.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
//

#import "virtualization.h"
#import "virtualization_helper.h"
#import "virtualization_view.h"

char *copyCString(NSString *nss)
Expand Down Expand Up @@ -1075,15 +1074,6 @@ bool requestStopVirtualMachine(void *machine, void *queue, void **error)
return queue;
}

typedef void (^vm_completion_handler_t)(NSError *);

vm_completion_handler_t makeVMCompletionHandler(void *completionHandler)
{
return Block_copy(^(NSError *err) {
virtualMachineCompletionHandler(completionHandler, err);
});
}

void startWithCompletionHandler(void *machine, void *queue, void *completionHandler)
{
if (@available(macOS 11, *)) {
Expand Down
3 changes: 2 additions & 1 deletion virtualization_13.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#pragma once

#import "virtualization_helper.h"
#import <Foundation/Foundation.h>
#import <Virtualization/Virtualization.h>

/* macOS 13 API */
Expand Down Expand Up @@ -41,3 +40,5 @@ void setAttachmentVZVirtioConsolePortConfiguration(void *consolePortConfig, void
void *newVZSpiceAgentPortAttachment();
void setSharesClipboardVZSpiceAgentPortAttachment(void *attachment, bool sharesClipboard);
const char *getSpiceAgentPortName();

void startWithOptionsCompletionHandler(void *machine, void *queue, void *options, void *completionHandler);
24 changes: 24 additions & 0 deletions virtualization_13.m
Original file line number Diff line number Diff line change
Expand Up @@ -415,3 +415,27 @@ void setSharesClipboardVZSpiceAgentPortAttachment(void *attachment, bool sharesC
#endif
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
}

/*!
@abstract Start a virtual machine with options.
@discussion
Start a virtual machine that is in either Stopped or Error state.
@param options Options used to control how the virtual machine is started.
@param completionHandler Block called after the virtual machine has been successfully started or on error.
The error parameter passed to the block is nil if the start was successful.
@seealso VZMacOSVirtualMachineStartOptions
*/
void startWithOptionsCompletionHandler(void *machine, void *queue, void *options, void *completionHandler)
{
#ifdef INCLUDE_TARGET_OSX_13
if (@available(macOS 13, *)) {
vm_completion_handler_t handler = makeVMCompletionHandler(completionHandler);
dispatch_sync((dispatch_queue_t)queue, ^{
[(VZVirtualMachine *)machine startWithOptions:options completionHandler:handler];
});
Block_release(handler);
return;
}
#endif
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
}
3 changes: 2 additions & 1 deletion virtualization_13_arm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#define NSURLComponents NSURLComponents

#import "virtualization_helper.h"
#import <Foundation/Foundation.h>
#import <Virtualization/Virtualization.h>

/* exported from cgo */
Expand All @@ -23,4 +22,6 @@ void *newVZLinuxRosettaDirectoryShare(void **error);
void linuxInstallRosetta(void *cgoHandler);
int availabilityVZLinuxRosettaDirectoryShare();

void *newVZMacOSVirtualMachineStartOptions(bool startUpFromMacOSRecovery);

#endif
17 changes: 16 additions & 1 deletion virtualization_13_arm64.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,19 @@ int availabilityVZLinuxRosettaDirectoryShare()
}
#endif
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
}
}

/*!
@abstract Options controlling startup behavior of a virtual machine using VZMacOSBootLoader.
*/
void *newVZMacOSVirtualMachineStartOptions(bool startUpFromMacOSRecovery)
{
#ifdef INCLUDE_TARGET_OSX_13
if (@available(macOS 13, *)) {
VZMacOSVirtualMachineStartOptions *opts = [[VZMacOSVirtualMachineStartOptions alloc] init];
[opts setStartUpFromMacOSRecovery:(BOOL)startUpFromMacOSRecovery];
return opts;
}
#endif
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
}
18 changes: 18 additions & 0 deletions virtualization_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package vz
#cgo darwin LDFLAGS: -lobjc -framework Foundation -framework Virtualization
# include "virtualization.h"
# include "virtualization_arm64.h"
# include "virtualization_13_arm64.h"
*/
import "C"
import (
Expand All @@ -25,6 +26,23 @@ import (
"github.com/Code-Hex/vz/v2/internal/progress"
)

// WithStartUpFromMacOSRecovery is an option to specifiy whether to start up
// from macOS Recovery for macOS VM.
//
// This is only supported on macOS 13 and newer, ErrUnsupportedOSVersion will
// be returned on older versions.
func WithStartUpFromMacOSRecovery(startInRecovery bool) VirtualMachineStartOption {
return func(vmso *virtualMachineStartOptions) error {
if macosMajorVersionLessThan(13) {
return ErrUnsupportedOSVersion
}
vmso.macOSVirtualMachineStartOptionsPtr = C.newVZMacOSVirtualMachineStartOptions(
C.bool(startInRecovery),
)
return nil
}
}

// MacHardwareModel describes a specific virtual Mac hardware model.
type MacHardwareModel struct {
pointer
Expand Down
13 changes: 13 additions & 0 deletions virtualization_helper.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#import <Availability.h>
#import <Foundation/Foundation.h>

#define RAISE_UNSUPPORTED_MACOS_EXCEPTION() \
do { \
Expand All @@ -18,3 +19,15 @@ typedef struct nbyteslice {
void *ptr;
int len;
} nbyteslice;

/* exported from cgo */
void virtualMachineCompletionHandler(void *cgoHandler, void *errPtr);

typedef void (^vm_completion_handler_t)(NSError *);

static inline vm_completion_handler_t makeVMCompletionHandler(void *completionHandler)
{
return Block_copy(^(NSError *err) {
virtualMachineCompletionHandler(completionHandler, err);
});
}