浏览代码

Merge pull request #6977 from naveenrajm7/script-sandbox-path

scripting : add vm registry suite
osy 4 月之前
父节点
当前提交
3dfcddcc79

+ 27 - 0
Scripting/UTM.sdef

@@ -564,6 +564,10 @@
         <record-type name="qemu argument" code="QeAr" description="QEMU argument configuration.">
         <record-type name="qemu argument" code="QeAr" description="QEMU argument configuration.">
           <property name="argument string" code="ArSt" type="text"
           <property name="argument string" code="ArSt" type="text"
             description="The QEMU argument as a string."/>
             description="The QEMU argument as a string."/>
+          <property name="file urls" code="FlUr" 
+            description="Optional URLs associated with this argument.">
+            <type type="file" list="yes"/>
+          </property>
         </record-type>
         </record-type>
         
         
         <record-type name="apple configuration" code="ApCf" description="Apple virtual machine configuration.">
         <record-type name="apple configuration" code="ApCf" description="Apple virtual machine configuration.">
@@ -714,4 +718,27 @@
           <direct-parameter description="The USB device to disconnect." type="usb device"/>
           <direct-parameter description="The USB device to disconnect." type="usb device"/>
         </command>
         </command>
     </suite>
     </suite>
+    
+    <suite name="UTM Registry Suite" code="UTMr" description="UTM virtual machine registry suite. Use this to update virtual machine registry.">
+           <access-group identifier="com.utmapp.UTM.vm-access" />
+           
+           <class-extension extends="virtual machine" description="Virtual machine registry.">
+               <property name="registry" code="ReGs" access="r"
+                   description="The registry of the virtual machine.">
+                   <type type="file" list="yes"/>
+               </property>
+               
+               <responds-to command="update registry">
+                   <cocoa method="updateRegistry:"/>
+               </responds-to>
+           </class-extension>
+           
+           <command name="update registry" code="UTMrUpDt" description="Update the registry of the virtual machine.">
+               <direct-parameter description="Virtual machine to update." type="virtual machine"/>
+               <parameter name="with" code="UpRg" description="The registry to update the virtual machine. Currently you can only change the shared directory with this!">
+                   <cocoa key="newRegistry"/>
+                   <type type="file" list="yes"/>
+               </parameter>
+           </command>
+    </suite>
 </dictionary>
 </dictionary>

+ 8 - 1
Scripting/UTMScriptingConfigImpl.swift

@@ -194,6 +194,10 @@ extension UTMScriptingConfigImpl {
         var serializedArgument: [AnyHashable: Any] = [
         var serializedArgument: [AnyHashable: Any] = [
             "argumentString": argument.string
             "argumentString": argument.string
         ]
         ]
+        // Only add fileUrls if it is not nil and contains URLs
+        if let fileUrls = argument.fileUrls, !fileUrls.isEmpty {
+            serializedArgument["fileUrls"] = fileUrls.map({ $0 as AnyHashable })
+        }
         
         
         return serializedArgument
         return serializedArgument
     }
     }
@@ -526,7 +530,10 @@ extension UTMScriptingConfigImpl {
         let additionalArguments = records.compactMap { record -> QEMUArgument? in
         let additionalArguments = records.compactMap { record -> QEMUArgument? in
             guard let argumentString = record["argumentString"] as? String else { return nil }
             guard let argumentString = record["argumentString"] as? String else { return nil }
             var argument = QEMUArgument(argumentString)
             var argument = QEMUArgument(argumentString)
-            
+            // fileUrls are used as required resources by QEMU.
+            if let fileUrls = record["fileUrls"] as? [URL] {
+                argument.fileUrls = fileUrls
+            }
             return argument
             return argument
         }
         }
         // Update entire additional arguments with new one.
         // Update entire additional arguments with new one.

+ 77 - 0
Scripting/UTMScriptingRegistryEntryImpl.swift

@@ -0,0 +1,77 @@
+//
+// Copyright © 2025 naveenrajm7. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import Foundation
+
+@objc extension UTMScriptingVirtualMachineImpl {
+    @objc var registry: [URL] {
+        let wrapper = UTMScriptingRegistryEntryImpl(vm.registryEntry)
+        return wrapper.serializeRegistry()
+    }
+    
+    @objc func updateRegistry(_ command: NSScriptCommand) {
+        let newRegistry = command.evaluatedArguments?["newRegistry"] as? [URL]
+        withScriptCommand(command) { [self] in
+            guard let newRegistry = newRegistry else {
+                throw ScriptingError.invalidParameter
+            }
+            let wrapper = UTMScriptingRegistryEntryImpl(vm.registryEntry)
+            try await wrapper.updateRegistry(from: newRegistry, qemuProcess)
+        }
+    }
+}
+
+@MainActor
+class UTMScriptingRegistryEntryImpl {
+    private(set) var registry: UTMRegistryEntry
+    
+    init(_ registry: UTMRegistryEntry) {
+        self.registry = registry
+    }
+    
+    func serializeRegistry() -> [URL] {
+        return registry.sharedDirectories.compactMap { $0.url }
+    }
+    
+    func updateRegistry(from fileUrls: [URL], _ system: UTMQemuSystem?) async throws {
+        // Clear all shared directories, we add all directories here
+        registry.removeAllSharedDirectories()
+        
+        // Add urls to the registry
+        for url in fileUrls {
+            // Start scoped access
+            let isScopedAccess = url.startAccessingSecurityScopedResource()
+            defer {
+                if isScopedAccess {
+                    url.stopAccessingSecurityScopedResource()
+                }
+            }
+            
+            // Get bookmark from UTM process
+            let standardBookmark = try url.bookmarkData()
+            let system = system ?? UTMProcess()
+            let (success, bookmark, path) = await system.accessData(withBookmark: standardBookmark, securityScoped: false)
+            guard let bookmark = bookmark, let _ = path, success else {
+                throw UTMQemuVirtualMachineError.accessDriveImageFailed
+            }
+            
+            // Store bookmark in registry
+            let file = UTMRegistryEntry.File(dummyFromPath: url.path, remoteBookmark: bookmark)
+            registry.appendSharedDirectory(file)
+        }
+        
+    }
+}

+ 6 - 0
Scripting/UTMScriptingVirtualMachineImpl.swift

@@ -90,6 +90,12 @@ class UTMScriptingVirtualMachineImpl: NSObject, UTMScriptable {
         }
         }
     }
     }
     
     
+    var qemuProcess: UTMQemuSystem? {
+        get async {
+            await (vm as? UTMQemuVirtualMachine)?.system
+        }
+    }
+    
     override var objectSpecifier: NSScriptObjectSpecifier? {
     override var objectSpecifier: NSScriptObjectSpecifier? {
         let appDescription = NSApplication.classDescription() as! NSScriptClassDescription
         let appDescription = NSApplication.classDescription() as! NSScriptClassDescription
         return NSUniqueIDSpecifier(containerClassDescription: appDescription,
         return NSUniqueIDSpecifier(containerClassDescription: appDescription,

+ 2 - 1
Services/UTMQemuVirtualMachine.swift

@@ -129,7 +129,8 @@ final class UTMQemuVirtualMachine: UTMSpiceVirtualMachine {
 
 
     private let qemuVM = QEMUVirtualMachine()
     private let qemuVM = QEMUVirtualMachine()
     
     
-    private var system: UTMQemuSystem? {
+    /// QEMU Process interface
+    var system: UTMQemuSystem? {
         get async {
         get async {
             await qemuVM.launcher as? UTMQemuSystem
             await qemuVM.launcher as? UTMQemuSystem
         }
         }

+ 4 - 0
Services/UTMRegistryEntry.swift

@@ -260,6 +260,10 @@ extension UTMRegistryEntry: UTMRegistryEntryDecodable {}
         }
         }
     }
     }
     
     
+    func appendSharedDirectory(_ file: File) {
+        sharedDirectories.append(file)
+    }
+    
     func removeAllSharedDirectories() {
     func removeAllSharedDirectories() {
         sharedDirectories = []
         sharedDirectories = []
     }
     }

+ 4 - 0
UTM.xcodeproj/project.pbxproj

@@ -272,6 +272,7 @@
 		B3DDF57226E9BBA300CE47F0 /* AltKit in Frameworks */ = {isa = PBXBuildFile; productRef = B3DDF57126E9BBA300CE47F0 /* AltKit */; };
 		B3DDF57226E9BBA300CE47F0 /* AltKit in Frameworks */ = {isa = PBXBuildFile; productRef = B3DDF57126E9BBA300CE47F0 /* AltKit */; };
 		CD77BE422CAB51B40074ADD2 /* UTMScriptingExportCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD77BE412CAB519F0074ADD2 /* UTMScriptingExportCommand.swift */; };
 		CD77BE422CAB51B40074ADD2 /* UTMScriptingExportCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD77BE412CAB519F0074ADD2 /* UTMScriptingExportCommand.swift */; };
 		CD77BE442CB38F060074ADD2 /* UTMScriptingImportCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD77BE432CB38F060074ADD2 /* UTMScriptingImportCommand.swift */; };
 		CD77BE442CB38F060074ADD2 /* UTMScriptingImportCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD77BE432CB38F060074ADD2 /* UTMScriptingImportCommand.swift */; };
+		CD84C2092D3B446D00829850 /* UTMScriptingRegistryEntryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD84C2082D3B446D00829850 /* UTMScriptingRegistryEntryImpl.swift */; };
 		CE020BA324AEDC7C00B44AB6 /* UTMData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE020BA224AEDC7C00B44AB6 /* UTMData.swift */; };
 		CE020BA324AEDC7C00B44AB6 /* UTMData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE020BA224AEDC7C00B44AB6 /* UTMData.swift */; };
 		CE020BA424AEDC7C00B44AB6 /* UTMData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE020BA224AEDC7C00B44AB6 /* UTMData.swift */; };
 		CE020BA424AEDC7C00B44AB6 /* UTMData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE020BA224AEDC7C00B44AB6 /* UTMData.swift */; };
 		CE020BA724AEDEF000B44AB6 /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = CE020BA624AEDEF000B44AB6 /* Logging */; };
 		CE020BA724AEDEF000B44AB6 /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = CE020BA624AEDEF000B44AB6 /* Logging */; };
@@ -1779,6 +1780,7 @@
 		C8958B6D243634DA002D86B4 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; };
 		C8958B6D243634DA002D86B4 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; };
 		CD77BE412CAB519F0074ADD2 /* UTMScriptingExportCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMScriptingExportCommand.swift; sourceTree = "<group>"; };
 		CD77BE412CAB519F0074ADD2 /* UTMScriptingExportCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMScriptingExportCommand.swift; sourceTree = "<group>"; };
 		CD77BE432CB38F060074ADD2 /* UTMScriptingImportCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMScriptingImportCommand.swift; sourceTree = "<group>"; };
 		CD77BE432CB38F060074ADD2 /* UTMScriptingImportCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMScriptingImportCommand.swift; sourceTree = "<group>"; };
+		CD84C2082D3B446D00829850 /* UTMScriptingRegistryEntryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMScriptingRegistryEntryImpl.swift; sourceTree = "<group>"; };
 		CE020BA224AEDC7C00B44AB6 /* UTMData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMData.swift; sourceTree = "<group>"; };
 		CE020BA224AEDC7C00B44AB6 /* UTMData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMData.swift; sourceTree = "<group>"; };
 		CE020BAA24AEE00000B44AB6 /* UTMLoggingSwift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMLoggingSwift.swift; sourceTree = "<group>"; };
 		CE020BAA24AEE00000B44AB6 /* UTMLoggingSwift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMLoggingSwift.swift; sourceTree = "<group>"; };
 		CE020BB524B14F8400B44AB6 /* UTMVirtualMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMVirtualMachine.swift; sourceTree = "<group>"; };
 		CE020BB524B14F8400B44AB6 /* UTMVirtualMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMVirtualMachine.swift; sourceTree = "<group>"; };
@@ -3027,6 +3029,7 @@
 				CEC794B9294924E300121A9F /* UTMScriptingSerialPortImpl.swift */,
 				CEC794B9294924E300121A9F /* UTMScriptingSerialPortImpl.swift */,
 				CE25124829BFDBA6000790AB /* UTMScriptingGuestFileImpl.swift */,
 				CE25124829BFDBA6000790AB /* UTMScriptingGuestFileImpl.swift */,
 				CE25124629BFDB87000790AB /* UTMScriptingGuestProcessImpl.swift */,
 				CE25124629BFDB87000790AB /* UTMScriptingGuestProcessImpl.swift */,
+				CD84C2082D3B446D00829850 /* UTMScriptingRegistryEntryImpl.swift */,
 				CE25124C29C55816000790AB /* UTMScriptingConfigImpl.swift */,
 				CE25124C29C55816000790AB /* UTMScriptingConfigImpl.swift */,
 				CE25125429C80CD4000790AB /* UTMScriptingCreateCommand.swift */,
 				CE25125429C80CD4000790AB /* UTMScriptingCreateCommand.swift */,
 				CD77BE432CB38F060074ADD2 /* UTMScriptingImportCommand.swift */,
 				CD77BE432CB38F060074ADD2 /* UTMScriptingImportCommand.swift */,
@@ -3724,6 +3727,7 @@
 				CEBE820D26A4C8E0007AAB12 /* VMWizardSummaryView.swift in Sources */,
 				CEBE820D26A4C8E0007AAB12 /* VMWizardSummaryView.swift in Sources */,
 				8401FDB2269E602000265F0D /* VMConfigAppleDriveDetailsView.swift in Sources */,
 				8401FDB2269E602000265F0D /* VMConfigAppleDriveDetailsView.swift in Sources */,
 				841619B428431DA5000034B2 /* UTMQemuConfigurationQEMU.swift in Sources */,
 				841619B428431DA5000034B2 /* UTMQemuConfigurationQEMU.swift in Sources */,
+				CD84C2092D3B446D00829850 /* UTMScriptingRegistryEntryImpl.swift in Sources */,
 				CEF0307626A2B40B00667B63 /* VMWizardHardwareView.swift in Sources */,
 				CEF0307626A2B40B00667B63 /* VMWizardHardwareView.swift in Sources */,
 				CED234EE254796E500ED0A57 /* NumberTextField.swift in Sources */,
 				CED234EE254796E500ED0A57 /* NumberTextField.swift in Sources */,
 				841619B028431952000034B2 /* UTMQemuConfigurationSystem.swift in Sources */,
 				841619B028431952000034B2 /* UTMQemuConfigurationSystem.swift in Sources */,