Skip to content

Commit b911e95

Browse files
committed
monitor: refactored UTMQemuManager to UTMQemuMonitor
De-couple the JSON RPC handling from QMP commands.
1 parent 2639cbd commit b911e95

16 files changed

+428
-325
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// Copyright © 2023 osy. All rights reserved.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//
16+
17+
#import "UTMQemuManager.h"
18+
#import "error.h"
19+
20+
NS_ASSUME_NONNULL_BEGIN
21+
22+
@interface UTMQemuManager (Protected)
23+
24+
@property (nonatomic, readwrite) BOOL isConnected;
25+
26+
- (__autoreleasing NSError *)errorForQerror:(Error *)qerr;
27+
- (BOOL)didGetUnhandledKey:(NSString *)key value:(id)value;
28+
29+
@end
30+
31+
NS_ASSUME_NONNULL_END

Managers/UTMQemuManager.h

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
#import <Foundation/Foundation.h>
1818
#import "UTMJSONStreamDelegate.h"
19-
#import "UTMQemuManagerDelegate.h"
19+
#import "UTMQemuMonitorDelegate.h"
2020

2121
@class UTMJSONStream;
2222
@class CSPort;
@@ -26,26 +26,11 @@ NS_ASSUME_NONNULL_BEGIN
2626
@interface UTMQemuManager : NSObject<UTMJSONStreamDelegate>
2727

2828
@property (nonatomic, readonly) UTMJSONStream *jsonStream;
29-
@property (nonatomic, weak) id<UTMQemuManagerDelegate> delegate;
3029
@property (nonatomic, readonly) BOOL isConnected;
3130

3231
- (instancetype)init NS_UNAVAILABLE;
3332
- (instancetype)initWithPort:(CSPort *)port NS_DESIGNATED_INITIALIZER;
3433

35-
- (BOOL)qmpEnterCommandModeWithError:(NSError * _Nullable __autoreleasing *)error;
36-
- (BOOL)continueBootWithError:(NSError * _Nullable __autoreleasing *)error;
37-
38-
- (void)qemuPowerDownWithCompletion:(void (^ _Nullable)(NSError * _Nullable))completion;
39-
- (void)qemuResetWithCompletion:(void (^ _Nullable)(NSError * _Nullable))completion;
40-
- (void)qemuStopWithCompletion:(void (^ _Nullable)(NSError * _Nullable))completion;
41-
- (void)qemuResumeWithCompletion:(void (^ _Nullable)(NSError * _Nullable))completion;
42-
- (void)qemuQuitWithCompletion:(void (^ _Nullable)(NSError * _Nullable))completion;
43-
- (void)qemuSaveStateWithCompletion:(void (^ _Nullable)(NSString * _Nullable, NSError * _Nullable))completion snapshotName:(NSString *)name;
44-
- (void)qemuDeleteStateWithCompletion:(void (^)(NSString * _Nullable, NSError * _Nullable))completion snapshotName:(NSString *)name;
45-
46-
- (void)mouseIndexForAbsolute:(BOOL)absolute withCompletion:(void (^)(int64_t, NSError * _Nullable))completion;
47-
- (void)mouseSelect:(int64_t)index withCompletion:(void (^)(NSString * _Nullable, NSError * _Nullable))completion;
48-
4934
@end
5035

5136
NS_ASSUME_NONNULL_END

Managers/UTMQemuManager.m

Lines changed: 10 additions & 243 deletions
Original file line numberDiff line numberDiff line change
@@ -14,105 +14,18 @@
1414
// limitations under the License.
1515
//
1616

17-
#import "UTMQemuManager.h"
17+
#import "UTMQemuManager-Protected.h"
1818
#import "UTMJSONStream.h"
1919
#import "UTMLogging.h"
20-
#import "qapi-commands.h"
2120
#import "qapi-emit-events.h"
22-
#import "qapi-events.h"
23-
#import "error.h"
2421

2522
extern NSString *const kUTMErrorDomain;
2623
const int64_t kRPCTimeout = (int64_t)10*NSEC_PER_SEC;
2724

28-
static void utm_shutdown_handler(bool guest, ShutdownCause reason, void *ctx) {
29-
UTMQemuManager *self = (__bridge UTMQemuManager *)ctx;
30-
[self.delegate qemuWillQuit:self guest:guest reason:reason];
31-
}
32-
33-
static void utm_powerdown_handler(void *ctx) {
34-
35-
}
36-
37-
static void utm_reset_handler(bool guest, ShutdownCause reason, void *ctx) {
38-
UTMQemuManager *self = (__bridge UTMQemuManager *)ctx;
39-
[self.delegate qemuHasReset:self guest:guest reason:reason];
40-
}
41-
42-
static void utm_stop_handler(void *ctx) {
43-
UTMQemuManager *self = (__bridge UTMQemuManager *)ctx;
44-
[self.delegate qemuHasStopped:self];
45-
}
46-
47-
static void utm_resume_handler(void *ctx) {
48-
UTMQemuManager *self = (__bridge UTMQemuManager *)ctx;
49-
[self.delegate qemuHasResumed:self];
50-
}
51-
52-
static void utm_suspend_handler(void *ctx) {
53-
UTMQemuManager *self = (__bridge UTMQemuManager *)ctx;
54-
[self.delegate qemuHasSuspended:self];
55-
}
56-
57-
static void utm_suspend_disk_handler(void *ctx) {
58-
59-
}
60-
61-
static void utm_wakeup_handler(void *ctx) {
62-
UTMQemuManager *self = (__bridge UTMQemuManager *)ctx;
63-
[self.delegate qemuHasWakeup:self];
64-
}
65-
66-
static void utm_watchdog_handler(WatchdogAction action, void *ctx) {
67-
68-
}
69-
70-
static void utm_guest_panicked_handler(GuestPanicAction action, bool has_info, GuestPanicInformation *info, void *ctx) {
71-
UTMQemuManager *self = (__bridge UTMQemuManager *)ctx;
72-
[self.delegate qemuError:self error:NSLocalizedString(@"Guest panic", @"UTMQemuManager")];
73-
}
74-
75-
static void utm_block_image_corrupted_handler(const char *device, bool has_node_name, const char *node_name, const char *msg, bool has_offset, int64_t offset, bool has_size, int64_t size, bool fatal, void *ctx) {
76-
UTMQemuManager *self = (__bridge UTMQemuManager *)ctx;
77-
if (fatal) {
78-
[self.delegate qemuError:self error:[NSString stringWithFormat:@"%s, %s: %s", device, node_name, msg]];
79-
}
80-
}
81-
82-
static void utm_block_io_error_handler(const char *device, bool has_node_name, const char *node_name, IoOperationType operation, BlockErrorAction action, bool has_nospace, bool nospace, const char *reason, void *ctx) {
83-
UTMQemuManager *self = (__bridge UTMQemuManager *)ctx;
84-
[self.delegate qemuError:self error:[NSString stringWithFormat:@"%s, %s: %s", device, node_name, reason]];
85-
}
86-
87-
static void utm_spice_connected_handler(SpiceBasicInfo *server, SpiceBasicInfo *client, void *ctx) {
88-
89-
}
90-
91-
static void utm_spice_initialized_handler(SpiceServerInfo *server, SpiceChannel *client, void *ctx) {
92-
93-
}
94-
95-
static void utm_spice_disconnected_handler(SpiceBasicInfo *server, SpiceBasicInfo *client, void *ctx) {
96-
97-
}
98-
99-
static void utm_spice_migrate_completed_handler(void *ctx) {
100-
101-
}
102-
103-
static void utm_migration_handler(MigrationStatus status, void *ctx) {
104-
105-
}
106-
107-
static void utm_migration_pass_handler(int64_t pass, void *ctx) {
108-
109-
}
110-
11125
typedef void(^rpcCompletionHandler_t)(NSDictionary *, NSError *);
11226

11327
@interface UTMQemuManager ()
11428

115-
@property (nonatomic, readwrite) BOOL isConnected;
11629
@property (nonatomic, nullable) rpcCompletionHandler_t rpcCallback;
11730
@property (nonatomic) UTMJSONStream *jsonStream;
11831
@property (nonatomic) dispatch_semaphore_t cmdLock;
@@ -121,6 +34,12 @@ @interface UTMQemuManager ()
12134

12235
@implementation UTMQemuManager
12336

37+
@synthesize isConnected = _isConnected;
38+
39+
- (void)setIsConnected:(BOOL)isConnected {
40+
_isConnected = isConnected;
41+
}
42+
12443
void qmp_rpc_call(CFDictionaryRef args, CFDictionaryRef *ret, Error **err, void *ctx) {
12544
UTMQemuManager *self = (__bridge UTMQemuManager *)ctx;
12645
dispatch_semaphore_t rpc_sema = dispatch_semaphore_create(0);
@@ -208,9 +127,7 @@ - (void)jsonStream:(UTMJSONStream *)stream receivedDictionary:(NSDictionary *)di
208127
const char *event = [dict[@"event"] cStringUsingEncoding:NSASCIIStringEncoding];
209128
qapi_event_dispatch(event, (__bridge CFTypeRef)dict, (__bridge void *)self);
210129
*stop = YES;
211-
} else if ([key isEqualToString:@"QMP"]) {
212-
UTMLog(@"Got QMP handshake: %@", dict);
213-
[self.delegate qemuQmpDidConnect:self];
130+
} else if ([self didGetUnhandledKey:key value:val]) {
214131
*stop = YES;
215132
}
216133
}];
@@ -220,158 +137,8 @@ - (__autoreleasing NSError *)errorForQerror:(Error *)qerr {
220137
return [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithCString:error_get_pretty(qerr) encoding:NSASCIIStringEncoding]}];
221138
}
222139

223-
- (BOOL)qmpEnterCommandModeWithError:(NSError * _Nullable __autoreleasing *)error {
224-
NSDictionary *cmd = @{
225-
@"execute": @"qmp_capabilities"
226-
};
227-
Error *qerr = NULL;
228-
qmp_rpc_call((__bridge CFDictionaryRef)cmd, NULL, &qerr, (__bridge void *)self);
229-
if (qerr != NULL) {
230-
if (error) {
231-
*error = [self errorForQerror:qerr];
232-
error_free(qerr);
233-
}
234-
return NO;
235-
} else {
236-
self.isConnected = YES;
237-
return YES;
238-
}
239-
}
240-
241-
- (BOOL)continueBootWithError:(NSError * _Nullable __autoreleasing *)error {
242-
Error *qerr = NULL;
243-
qmp_cont(&qerr, (__bridge void *)self);
244-
if (qerr != NULL) {
245-
if (error) {
246-
*error = [self errorForQerror:qerr];
247-
error_free(qerr);
248-
}
249-
self.isConnected = NO;
250-
return NO;
251-
} else {
252-
return YES;
253-
}
254-
}
255-
256-
- (void)qmpPowerCommand:(NSString *)command completion:(void (^ _Nullable)(NSError * _Nullable))completion {
257-
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
258-
Error *qerr = NULL;
259-
NSError *err;
260-
NSDictionary *cmd = @{
261-
@"execute": command
262-
};
263-
qmp_rpc_call((__bridge CFDictionaryRef)cmd, NULL, &qerr, (__bridge void *)self);
264-
if (qerr) {
265-
err = [self errorForQerror:qerr];
266-
error_free(qerr);
267-
}
268-
if (completion) {
269-
completion(err);
270-
}
271-
});
272-
}
273-
274-
- (void)qmpHmpCommand:(NSString *)cmd completion:(void (^)(NSString * _Nullable, NSError * _Nullable))completion {
275-
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
276-
Error *qerr = NULL;
277-
NSError *err;
278-
NSString *result;
279-
char *res;
280-
res = qmp_human_monitor_command([cmd cStringUsingEncoding:NSASCIIStringEncoding], false, 0, &qerr, (__bridge void *)self);
281-
if (res) {
282-
result = [NSString stringWithCString:res encoding:NSASCIIStringEncoding];
283-
g_free(res);
284-
}
285-
if (qerr) {
286-
err = [self errorForQerror:qerr];
287-
error_free(qerr);
288-
}
289-
if (completion) {
290-
completion(result, err);
291-
}
292-
});
293-
}
294-
295-
- (void)qemuPowerDownWithCompletion:(void (^ _Nullable)(NSError * _Nullable))completion {
296-
[self qmpPowerCommand:@"system_powerdown" completion:completion];
297-
}
298-
299-
- (void)qemuResetWithCompletion:(void (^ _Nullable)(NSError * _Nullable))completion {
300-
[self qmpPowerCommand:@"system_reset" completion:completion];
301-
}
302-
303-
- (void)qemuStopWithCompletion:(void (^ _Nullable)(NSError * _Nullable))completion {
304-
[self qmpPowerCommand:@"stop" completion:completion];
305-
}
306-
307-
- (void)qemuResumeWithCompletion:(void (^ _Nullable)(NSError * _Nullable))completion {
308-
[self qmpPowerCommand:@"cont" completion:completion];
309-
}
310-
311-
- (void)qemuQuitWithCompletion:(void (^ _Nullable)(NSError * _Nullable))completion {
312-
[self qmpPowerCommand:@"quit" completion:completion];
313-
}
314-
315-
- (void)qemuSaveStateWithCompletion:(void (^)(NSString * _Nullable, NSError * _Nullable))completion snapshotName:(NSString *)name {
316-
NSString *cmd = [NSString stringWithFormat:@"savevm %@", name];
317-
[self qmpHmpCommand:cmd completion:completion];
318-
}
319-
320-
- (void)qemuDeleteStateWithCompletion:(void (^)(NSString * _Nullable, NSError * _Nullable))completion snapshotName:(NSString *)name {
321-
NSString *cmd = [NSString stringWithFormat:@"delvm %@", name];
322-
[self qmpHmpCommand:cmd completion:completion];
323-
}
324-
325-
- (void)mouseIndexForAbsolute:(BOOL)absolute withCompletion:(void (^)(int64_t, NSError * _Nullable))completion {
326-
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
327-
MouseInfoList *info = NULL;
328-
Error *qerr = NULL;
329-
int64_t index = -1;
330-
NSError *err;
331-
info = qmp_query_mice(&qerr, (__bridge void *)self);
332-
if (qerr) {
333-
err = [self errorForQerror:qerr];
334-
error_free(qerr);
335-
}
336-
if (info) {
337-
for (MouseInfoList *list = info; list; list = list->next) {
338-
if (list->value->absolute == absolute) {
339-
index = list->value->index;
340-
break;
341-
}
342-
}
343-
qapi_free_MouseInfoList(info);
344-
}
345-
if (completion) {
346-
completion(index, err);
347-
}
348-
});
349-
}
350-
351-
- (void)mouseSelect:(int64_t)index withCompletion:(void (^)(NSString * _Nullable, NSError * _Nullable))completion {
352-
NSString *cmd = [NSString stringWithFormat:@"mouse_set %lld", index];
353-
[self qmpHmpCommand:cmd completion:completion];
140+
- (BOOL)didGetUnhandledKey:(NSString *)key value:(id)value {
141+
return NO;
354142
}
355143

356144
@end
357-
358-
qapi_enum_handler_registry qapi_enum_handler_registry_data = {
359-
.qapi_shutdown_handler = utm_shutdown_handler,
360-
.qapi_powerdown_handler = utm_powerdown_handler,
361-
.qapi_reset_handler = utm_reset_handler,
362-
.qapi_stop_handler = utm_stop_handler,
363-
.qapi_resume_handler = utm_resume_handler,
364-
.qapi_suspend_handler = utm_suspend_handler,
365-
.qapi_suspend_disk_handler = utm_suspend_disk_handler,
366-
.qapi_wakeup_handler = utm_wakeup_handler,
367-
.qapi_watchdog_handler = utm_watchdog_handler,
368-
.qapi_guest_panicked_handler = utm_guest_panicked_handler,
369-
.qapi_block_image_corrupted_handler = utm_block_image_corrupted_handler,
370-
.qapi_block_io_error_handler = utm_block_io_error_handler,
371-
.qapi_spice_connected_handler = utm_spice_connected_handler,
372-
.qapi_spice_initialized_handler = utm_spice_initialized_handler,
373-
.qapi_spice_disconnected_handler = utm_spice_disconnected_handler,
374-
.qapi_spice_migrate_completed_handler = utm_spice_migrate_completed_handler,
375-
.qapi_migration_handler = utm_migration_handler,
376-
.qapi_migration_pass_handler = utm_migration_pass_handler,
377-
};

Managers/UTMQemuManager+BlockDevices.h renamed to Managers/UTMQemuMonitor+BlockDevices.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
// limitations under the License.
1515
//
1616

17-
#import "UTMQemuManager.h"
17+
#import "UTMQemuMonitor.h"
1818

1919
NS_ASSUME_NONNULL_BEGIN
2020

21-
@interface UTMQemuManager (BlockDevices)
21+
@interface UTMQemuMonitor (BlockDevices)
2222

2323
@property (nonatomic, readonly, nullable) NSDictionary<NSString *, NSString *> *removableDrives;
2424

Managers/UTMQemuManager+BlockDevices.m renamed to Managers/UTMQemuMonitor+BlockDevices.m

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,13 @@
1414
// limitations under the License.
1515
//
1616

17-
#import "UTMQemuManager+BlockDevices.h"
17+
#import "UTMQemuMonitor+BlockDevices.h"
18+
#import "UTMQemuManager-Protected.h"
1819
#import "UTMLogging.h"
1920
#import "qapi-commands.h"
2021
#import "error.h"
2122

22-
@interface UTMQemuManager ()
23-
24-
- (__autoreleasing NSError *)errorForQerror:(Error *)qerr;
25-
26-
@end
27-
28-
@implementation UTMQemuManager (BlockDevices)
23+
@implementation UTMQemuMonitor (BlockDevices)
2924

3025
- (NSDictionary<NSString *, NSString *> *)removableDrives {
3126
if (!self.isConnected) {

0 commit comments

Comments
 (0)