Skip to content

Commit

Permalink
guestagent: support setting guest time
Browse files Browse the repository at this point in the history
Automatically set guest time on GA connect and VM resume.

Resolves #3218
  • Loading branch information
osy committed Mar 12, 2023
1 parent c34b531 commit d0d3b1b
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 0 deletions.
6 changes: 6 additions & 0 deletions Managers/UTMQemuGuestAgent.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ NS_ASSUME_NONNULL_BEGIN
/// - Parameter completion: Callback to run on completion
- (void)synchronizeWithCompletion:(void (^ _Nullable)(NSError * _Nullable))completion;

/// Set guest time
/// - Parameters:
/// - time: time in seconds, relative to the Epoch of 1970-01-01 in UTC.
/// - completion: Callback to run on completion
- (void)guestSetTime:(NSTimeInterval)time withCompletion:(void (^ _Nullable)(NSError * _Nullable))completion;

@end

NS_ASSUME_NONNULL_END
13 changes: 13 additions & 0 deletions Managers/UTMQemuGuestAgent.m
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,17 @@ - (void)_withSynchronizeBlock:(NSError * _Nullable (^)(void))block withCompletio
});
}

- (void)guestSetTime:(NSTimeInterval)time withCompletion:(void (^ _Nullable)(NSError * _Nullable))completion {
int64_t timeNanoseconds = (int64_t)(time * NSEC_PER_SEC);
[self _withSynchronizeBlock:^{
Error *qerr = NULL;
qmp_guest_set_time(true, timeNanoseconds, &qerr, (__bridge void *)self);
if (qerr) {
return [self errorForQerror:qerr];
} else {
return (NSError *)nil;
}
} withCompletion:completion];
}

@end
3 changes: 3 additions & 0 deletions Managers/UTMQemuVirtualMachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ NS_ASSUME_NONNULL_BEGIN

@property (nonatomic, weak, nullable) id<UTMSpiceIODelegate> ioDelegate;

/// If non-null, provides access to the QEMU guest agent interface
@property (nonatomic, readonly, nullable) UTMQemuGuestAgent *guestAgent;

/// Set to true to request guest tools install.
///
/// This property is observable and must only be accessed on the main thread.
Expand Down
36 changes: 36 additions & 0 deletions Managers/UTMQemuVirtualMachine.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#import "UTMQemuVirtualMachine.h"
#import "UTMQemuVirtualMachine+SPICE.h"
#import "UTMQemuMonitor.h"
#import "UTMQemuGuestAgent.h"
#import "UTMQemuSystem.h"
#import "UTMSpiceIO.h"
#import "UTMLogging.h"
Expand All @@ -33,9 +34,12 @@
extern NSString *const kUTMBundleConfigFilename;
NSString *const kSuspendSnapshotName = @"suspend";

static void *SpiceIoServiceGuestAgentContext = &SpiceIoServiceGuestAgentContext;

@interface UTMQemuVirtualMachine () <UTMLoggingDelegate, UTMQemuMonitorDelegate>

@property (nonatomic, readwrite, nullable) UTMQemuMonitor *qemu;
@property (nonatomic, readwrite, nullable) UTMQemuGuestAgent *guestAgent;
@property (nonatomic, readwrite, nullable) UTMQemuSystem *system;
@property (nonatomic, readwrite, nullable) UTMSpiceIO *ioService;
@property (nonatomic, weak) id<UTMSpiceIODelegate> ioServiceDelegate;
Expand Down Expand Up @@ -188,6 +192,10 @@ - (void)_vmStartWithCompletion:(void (^)(NSError * _Nullable))completion {
self.ioService = [[UTMSpiceIO alloc] initWithConfiguration:self.config];
self.ioService.delegate = self.ioServiceDelegate;
self.ioServiceDelegate = nil;
[self.ioService addObserver:self
forKeyPath:@"qemuGuestAgent"
options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionInitial
context:SpiceIoServiceGuestAgentContext];

NSError *spiceError;
if (![self.ioService startWithError:&spiceError]) {
Expand Down Expand Up @@ -336,6 +344,7 @@ - (void)_vmStopForce:(BOOL)force completion:(void (^)(NSError * _Nullable))compl
}
self.qemu.delegate = nil;
self.qemu = nil;
[self.ioService removeObserver:self forKeyPath:@"qemuGuestAgent" context:SpiceIoServiceGuestAgentContext];
self.ioService = nil;

if (force || dispatch_semaphore_wait(self.qemuDidExitEvent, dispatch_time(DISPATCH_TIME_NOW, kStopTimeout)) != 0) {
Expand Down Expand Up @@ -586,6 +595,32 @@ - (void)vmGuestPowerDownWithCompletion:(void (^)(NSError * _Nullable))completion
});
}

#pragma mark - QEMU Guest agent

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (context == SpiceIoServiceGuestAgentContext) {
UTMQemuGuestAgent *guestAgent = ((UTMSpiceIO *)object).qemuGuestAgent;
if (guestAgent == nil && self.guestAgent != nil) {
[self _didDisconnectGuestAgent:self.guestAgent];
}
if (guestAgent != nil) {
[self _didConnectGuestAgent:guestAgent];
}
self.guestAgent = guestAgent;
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}

- (void)_didConnectGuestAgent:(UTMQemuGuestAgent *)guestAgent {
UTMLog(@"QEMU guest agent has connected.");
[guestAgent guestSetTime:NSDate.now.timeIntervalSince1970 withCompletion:nil];
}

- (void)_didDisconnectGuestAgent:(UTMQemuGuestAgent *)guestAgent {
UTMLog(@"QEMU guest agent has disconnected.");
}

#pragma mark - Qemu manager delegate

- (void)qemuHasWakeup:(UTMQemuMonitor *)monitor {
Expand All @@ -594,6 +629,7 @@ - (void)qemuHasWakeup:(UTMQemuMonitor *)monitor {

- (void)qemuHasResumed:(UTMQemuMonitor *)monitor {
UTMLog(@"qemuHasResumed");
[self.guestAgent guestSetTime:NSDate.now.timeIntervalSince1970 withCompletion:nil];
}

- (void)qemuHasStopped:(UTMQemuMonitor *)monitor {
Expand Down

0 comments on commit d0d3b1b

Please sign in to comment.