2222#import " UTMQemuVirtualMachine.h"
2323#import " UTMQemuVirtualMachine+SPICE.h"
2424#import " UTMQemuMonitor.h"
25+ #import " UTMQemuGuestAgent.h"
2526#import " UTMQemuSystem.h"
2627#import " UTMSpiceIO.h"
2728#import " UTMLogging.h"
3334extern NSString *const kUTMBundleConfigFilename ;
3435NSString *const kSuspendSnapshotName = @" suspend" ;
3536
37+ static void *SpiceIoServiceGuestAgentContext = &SpiceIoServiceGuestAgentContext;
38+
3639@interface UTMQemuVirtualMachine () <UTMLoggingDelegate, UTMQemuMonitorDelegate>
3740
3841@property (nonatomic , readwrite , nullable ) UTMQemuMonitor *qemu;
42+ @property (nonatomic , readwrite , nullable ) UTMQemuGuestAgent *guestAgent;
3943@property (nonatomic , readwrite , nullable ) UTMQemuSystem *system;
4044@property (nonatomic , readwrite , nullable ) UTMSpiceIO *ioService;
4145@property (nonatomic , weak ) id <UTMSpiceIODelegate> ioServiceDelegate;
@@ -188,6 +192,10 @@ - (void)_vmStartWithCompletion:(void (^)(NSError * _Nullable))completion {
188192 self.ioService = [[UTMSpiceIO alloc ] initWithConfiguration: self .config];
189193 self.ioService .delegate = self.ioServiceDelegate ;
190194 self.ioServiceDelegate = nil ;
195+ [self .ioService addObserver: self
196+ forKeyPath: @" qemuGuestAgent"
197+ options: NSKeyValueObservingOptionNew|NSKeyValueObservingOptionInitial
198+ context: SpiceIoServiceGuestAgentContext];
191199
192200 NSError *spiceError;
193201 if (![self .ioService startWithError: &spiceError]) {
@@ -336,6 +344,7 @@ - (void)_vmStopForce:(BOOL)force completion:(void (^)(NSError * _Nullable))compl
336344 }
337345 self.qemu .delegate = nil ;
338346 self.qemu = nil ;
347+ [self .ioService removeObserver: self forKeyPath: @" qemuGuestAgent" context: SpiceIoServiceGuestAgentContext];
339348 self.ioService = nil ;
340349
341350 if (force || dispatch_semaphore_wait (self.qemuDidExitEvent , dispatch_time (DISPATCH_TIME_NOW, kStopTimeout )) != 0 ) {
@@ -586,6 +595,32 @@ - (void)vmGuestPowerDownWithCompletion:(void (^)(NSError * _Nullable))completion
586595 });
587596}
588597
598+ #pragma mark - QEMU Guest agent
599+
600+ - (void )observeValueForKeyPath : (NSString *)keyPath ofObject : (id )object change : (NSDictionary <NSKeyValueChangeKey,id> *)change context : (void *)context {
601+ if (context == SpiceIoServiceGuestAgentContext) {
602+ UTMQemuGuestAgent *guestAgent = ((UTMSpiceIO *)object).qemuGuestAgent ;
603+ if (guestAgent == nil && self.guestAgent != nil ) {
604+ [self _didDisconnectGuestAgent: self .guestAgent];
605+ }
606+ if (guestAgent != nil ) {
607+ [self _didConnectGuestAgent: guestAgent];
608+ }
609+ self.guestAgent = guestAgent;
610+ } else {
611+ [super observeValueForKeyPath: keyPath ofObject: object change: change context: context];
612+ }
613+ }
614+
615+ - (void )_didConnectGuestAgent : (UTMQemuGuestAgent *)guestAgent {
616+ UTMLog (@" QEMU guest agent has connected." );
617+ [guestAgent guestSetTime: NSDate .now.timeIntervalSince1970 withCompletion: nil ];
618+ }
619+
620+ - (void )_didDisconnectGuestAgent : (UTMQemuGuestAgent *)guestAgent {
621+ UTMLog (@" QEMU guest agent has disconnected." );
622+ }
623+
589624#pragma mark - Qemu manager delegate
590625
591626- (void )qemuHasWakeup : (UTMQemuMonitor *)monitor {
@@ -594,6 +629,7 @@ - (void)qemuHasWakeup:(UTMQemuMonitor *)monitor {
594629
595630- (void )qemuHasResumed : (UTMQemuMonitor *)monitor {
596631 UTMLog (@" qemuHasResumed" );
632+ [self .guestAgent guestSetTime: NSDate .now.timeIntervalSince1970 withCompletion: nil ];
597633}
598634
599635- (void )qemuHasStopped : (UTMQemuMonitor *)monitor {
0 commit comments