diff --git a/Scripting/UTM.sdef b/Scripting/UTM.sdef
index 234408ce4..8cf6bf87f 100644
--- a/Scripting/UTM.sdef
+++ b/Scripting/UTM.sdef
@@ -561,6 +561,10 @@
+
+
+
@@ -708,4 +712,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Scripting/UTMScriptingConfigImpl.swift b/Scripting/UTMScriptingConfigImpl.swift
index b00edca15..dc3a633cd 100644
--- a/Scripting/UTMScriptingConfigImpl.swift
+++ b/Scripting/UTMScriptingConfigImpl.swift
@@ -193,6 +193,10 @@ extension UTMScriptingConfigImpl {
var serializedArgument: [AnyHashable: Any] = [
"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
}
@@ -517,7 +521,10 @@ extension UTMScriptingConfigImpl {
let additionalArguments = records.compactMap { record -> QEMUArgument? in
guard let argumentString = record["argumentString"] as? String else { return nil }
var argument = QEMUArgument(argumentString)
-
+ // fileUrls are used as required resources by QEMU.
+ if let fileUrls = record["fileUrls"] as? [URL] {
+ argument.fileUrls = fileUrls
+ }
return argument
}
// Update entire additional arguments with new one.
diff --git a/Scripting/UTMScriptingRegistryEntryImpl.swift b/Scripting/UTMScriptingRegistryEntryImpl.swift
new file mode 100644
index 000000000..8097a1173
--- /dev/null
+++ b/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)
+ }
+
+ }
+}
diff --git a/Scripting/UTMScriptingVirtualMachineImpl.swift b/Scripting/UTMScriptingVirtualMachineImpl.swift
index 51302848f..d3b48634c 100644
--- a/Scripting/UTMScriptingVirtualMachineImpl.swift
+++ b/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? {
let appDescription = NSApplication.classDescription() as! NSScriptClassDescription
return NSUniqueIDSpecifier(containerClassDescription: appDescription,
diff --git a/Services/UTMQemuVirtualMachine.swift b/Services/UTMQemuVirtualMachine.swift
index dd9a34253..8be0c5d4b 100644
--- a/Services/UTMQemuVirtualMachine.swift
+++ b/Services/UTMQemuVirtualMachine.swift
@@ -129,7 +129,8 @@ final class UTMQemuVirtualMachine: UTMSpiceVirtualMachine {
private let qemuVM = QEMUVirtualMachine()
- private var system: UTMQemuSystem? {
+ /// QEMU Process interface
+ var system: UTMQemuSystem? {
get async {
await qemuVM.launcher as? UTMQemuSystem
}
diff --git a/Services/UTMRegistryEntry.swift b/Services/UTMRegistryEntry.swift
index 73d21f7c9..982acc682 100644
--- a/Services/UTMRegistryEntry.swift
+++ b/Services/UTMRegistryEntry.swift
@@ -260,6 +260,10 @@ extension UTMRegistryEntry: UTMRegistryEntryDecodable {}
}
}
+ func appendSharedDirectory(_ file: File) {
+ sharedDirectories.append(file)
+ }
+
func removeAllSharedDirectories() {
sharedDirectories = []
}
diff --git a/UTM.xcodeproj/project.pbxproj b/UTM.xcodeproj/project.pbxproj
index c881e294e..6b83570cf 100644
--- a/UTM.xcodeproj/project.pbxproj
+++ b/UTM.xcodeproj/project.pbxproj
@@ -272,6 +272,7 @@
B3DDF57226E9BBA300CE47F0 /* AltKit in Frameworks */ = {isa = PBXBuildFile; productRef = B3DDF57126E9BBA300CE47F0 /* AltKit */; };
CD77BE422CAB51B40074ADD2 /* UTMScriptingExportCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD77BE412CAB519F0074ADD2 /* UTMScriptingExportCommand.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 */; };
CE020BA424AEDC7C00B44AB6 /* UTMData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE020BA224AEDC7C00B44AB6 /* UTMData.swift */; };
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 = ""; };
CD77BE412CAB519F0074ADD2 /* UTMScriptingExportCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMScriptingExportCommand.swift; sourceTree = ""; };
CD77BE432CB38F060074ADD2 /* UTMScriptingImportCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMScriptingImportCommand.swift; sourceTree = ""; };
+ CD84C2082D3B446D00829850 /* UTMScriptingRegistryEntryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMScriptingRegistryEntryImpl.swift; sourceTree = ""; };
CE020BA224AEDC7C00B44AB6 /* UTMData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMData.swift; sourceTree = ""; };
CE020BAA24AEE00000B44AB6 /* UTMLoggingSwift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMLoggingSwift.swift; sourceTree = ""; };
CE020BB524B14F8400B44AB6 /* UTMVirtualMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMVirtualMachine.swift; sourceTree = ""; };
@@ -3027,6 +3029,7 @@
CEC794B9294924E300121A9F /* UTMScriptingSerialPortImpl.swift */,
CE25124829BFDBA6000790AB /* UTMScriptingGuestFileImpl.swift */,
CE25124629BFDB87000790AB /* UTMScriptingGuestProcessImpl.swift */,
+ CD84C2082D3B446D00829850 /* UTMScriptingRegistryEntryImpl.swift */,
CE25124C29C55816000790AB /* UTMScriptingConfigImpl.swift */,
CE25125429C80CD4000790AB /* UTMScriptingCreateCommand.swift */,
CD77BE432CB38F060074ADD2 /* UTMScriptingImportCommand.swift */,
@@ -3724,6 +3727,7 @@
CEBE820D26A4C8E0007AAB12 /* VMWizardSummaryView.swift in Sources */,
8401FDB2269E602000265F0D /* VMConfigAppleDriveDetailsView.swift in Sources */,
841619B428431DA5000034B2 /* UTMQemuConfigurationQEMU.swift in Sources */,
+ CD84C2092D3B446D00829850 /* UTMScriptingRegistryEntryImpl.swift in Sources */,
CEF0307626A2B40B00667B63 /* VMWizardHardwareView.swift in Sources */,
CED234EE254796E500ED0A57 /* NumberTextField.swift in Sources */,
841619B028431952000034B2 /* UTMQemuConfigurationSystem.swift in Sources */,