vagrant_utm 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,6 +13,25 @@ module VagrantPlugins
13
13
  @logger = Log4r::Logger.new("vagrant::provider::utm::version_4_6")
14
14
  end
15
15
 
16
+ # Implement clear_shared_folders
17
+ def clear_shared_folders
18
+ # Get the list of shared folders
19
+ shared_folders = read_shared_folders
20
+ # Get the args to remove the shared folders
21
+ script_path = @script_path.join("read_shared_folders_args.js")
22
+ cmd = ["osascript", script_path.to_s, @uuid, "--ids", shared_folders.join(",")]
23
+ output = execute_shell(*cmd)
24
+ result = JSON.parse(output)
25
+ return unless result["status"]
26
+
27
+ # Flatten the list of args and build the command
28
+ sf_args = result["result"].flatten
29
+ return unless sf_args.any?
30
+
31
+ command = ["remove_qemu_additional_args.applescript", @uuid, "--args", *sf_args]
32
+ execute_osa_script(command)
33
+ end
34
+
16
35
  def import(utm)
17
36
  utm = Vagrant::Util::Platform.windows_path(utm)
18
37
 
@@ -36,6 +55,54 @@ module VagrantPlugins
36
55
  command = ["export_vm.applescript", @uuid, path]
37
56
  execute_osa_script(command)
38
57
  end
58
+
59
+ def read_shared_folders
60
+ @logger.debug("Reading shared folders")
61
+ script_path = @script_path.join("read_shared_folders.js")
62
+ cmd = ["osascript", script_path.to_s, @uuid]
63
+ output = execute_shell(*cmd)
64
+ result = JSON.parse(output)
65
+ return unless result["status"]
66
+
67
+ # Return the list of shared folders names(id)
68
+ result["result"]
69
+ end
70
+
71
+ def share_folders(folders)
72
+ # sync folder cleanup will call clear_shared_folders
73
+ # This is just a precaution, to make sure we don't
74
+ # have duplicate shared folders
75
+ shared_folders = read_shared_folders
76
+ @logger.debug("Shared folders: #{shared_folders}")
77
+ @logger.debug("Sharing folders: #{folders}")
78
+
79
+ folders.each do |folder|
80
+ # Skip if the folder is already shared
81
+ next if shared_folders.include?(folder[:name])
82
+
83
+ args = ["--id", folder[:name],
84
+ "--dir", folder[:hostpath]]
85
+ command = ["add_folder_share.applescript", @uuid, *args]
86
+ execute_osa_script(command)
87
+ end
88
+ end
89
+
90
+ def unshare_folders(folders)
91
+ @logger.debug("Unsharing folder: #{folder[:name]}")
92
+ # Get the args to remove the shared folders
93
+ script_path = @script_path.join("read_shared_folders_args.js")
94
+ cmd = ["osascript", script_path.to_s, @uuid, "--ids", folders.join(",")]
95
+ output = execute_shell(*cmd)
96
+ result = JSON.parse(output)
97
+ return unless result["status"]
98
+
99
+ # Flatten the list of args and build the command
100
+ sf_args = result["result"].flatten
101
+ return unless sf_args.any?
102
+
103
+ command = ["remove_qemu_additional_args.applescript", @uuid, "--args", *sf_args]
104
+ execute_osa_script(command)
105
+ end
39
106
  end
40
107
  end
41
108
  end
@@ -33,6 +33,12 @@ module VagrantPlugins
33
33
  Config
34
34
  end
35
35
 
36
+ # Register the synced folder implementation
37
+ synced_folder(:utm) do
38
+ require_relative "synced_folder"
39
+ SyncedFolder
40
+ end
41
+
36
42
  # Register capabilities
37
43
  provider_capability(:utm, :forwarded_ports) do
38
44
  require_relative "cap"
@@ -44,6 +50,21 @@ module VagrantPlugins
44
50
  Cap
45
51
  end
46
52
 
53
+ synced_folder_capability(:utm, "mount_options") do
54
+ require_relative "cap/mount_options"
55
+ Cap::MountOptions
56
+ end
57
+
58
+ synced_folder_capability(:utm, "mount_type") do
59
+ require_relative "cap/mount_options"
60
+ Cap::MountOptions
61
+ end
62
+
63
+ synced_folder_capability(:utm, "mount_name") do
64
+ require_relative "cap/mount_options"
65
+ Cap::MountOptions
66
+ end
67
+
47
68
  # Register the command
48
69
  ## Start machine as a snapshot and do not save changes to disk
49
70
  command "disposable" do
@@ -0,0 +1,96 @@
1
+ ---
2
+ -- add_directory_share.applescript
3
+ -- This script adds QEMU arguments for directory sharing in UTM (QEMU) for given id and directory pairs.
4
+ -- Usage: osascript add_directory_share.applescript UUID --id <ID1> --dir <DIR1> --id <ID2> --dir <DIR2> ...
5
+ -- Example: osascript add_directory_share.applescript UUID --id no1 --dir "/path/to/dir1" --id no2 --dir "/path/to/dir2"
6
+
7
+ -- Function to create QEMU arguments for directory sharing
8
+ on createQemuArgsForDir(dirId, dirPath)
9
+
10
+ -- Prepare the QEMU argument strings
11
+ set fsdevArgStr to "-fsdev local,id=" & dirId & ",path=" & dirPath & ",security_model=mapped-xattr"
12
+ set deviceArgStr to "-device virtio-9p-pci,fsdev=" & dirId & ",mount_tag=" & dirId
13
+
14
+ return {fsdevArgStr, deviceArgStr}
15
+ end createQemuArgsForDir
16
+
17
+ -- Main script
18
+ on run argv
19
+ -- VM id is assumed to be the first argument
20
+ set vmId to item 1 of argv
21
+
22
+ -- Initialize variables
23
+ set idList to {} --
24
+ set dirList to {}
25
+ set idFlag to false
26
+ set dirFlag to false
27
+
28
+ -- Parse arguments
29
+ repeat with i from 2 to (count of argv)
30
+ set currentArg to item i of argv
31
+ if currentArg is "--id" then
32
+ set idFlag to true
33
+ set dirFlag to false
34
+ else if currentArg is "--dir" then
35
+ set dirFlag to true
36
+ set idFlag to false
37
+ else if idFlag then
38
+ set end of idList to currentArg
39
+ set idFlag to false
40
+ else if dirFlag then
41
+ set end of dirList to currentArg
42
+ set dirFlag to false
43
+ end if
44
+ end repeat
45
+
46
+ -- Ensure the lists are of the same length
47
+ if (count of idList) is not (count of dirList) then
48
+ error "The number of IDs and directories must be the same."
49
+ end if
50
+
51
+ -- Initialize the list of QEMU arguments
52
+ set qemuNewArgs to {}
53
+
54
+ -- Initialize the directory list
55
+ set directoryList to {}
56
+
57
+ -- Create QEMU arguments for each directory
58
+ repeat with i from 1 to (count of dirList)
59
+ set dirPath to item i of dirList
60
+ set dirId to item i of idList
61
+ set dirURL to POSIX file dirPath
62
+ set {fsdevArgStr, deviceArgStr} to createQemuArgsForDir(dirId, dirPath)
63
+
64
+ -- add the directory file obj to the list
65
+ set end of directoryList to dirURL
66
+ -- append the arguments to the list
67
+ set end of qemuNewArgs to {fsdevArg:fsdevArgStr, deviceArg:deviceArgStr, dirURL:dirURL}
68
+ end repeat
69
+
70
+ -- Example usage in UTM
71
+ tell application "UTM"
72
+ set vm to virtual machine id vmId
73
+ set config to configuration of vm
74
+
75
+ -- Get the current QEMU additional arguments
76
+ set qemuAddArgs to qemu additional arguments of config
77
+
78
+ -- Add the new arguments to the existing ones
79
+ repeat with arg in qemuNewArgs
80
+ -- SKIP: adding file urls to qemu args file urls , since it is not necessary. UTM#6977
81
+ set end of qemuAddArgs to {argument string:fsdevArg of arg}
82
+ set end of qemuAddArgs to {argument string:deviceArg of arg}
83
+ end repeat
84
+
85
+ -- Update the configuration with the new arguments list
86
+ set qemu additional arguments of config to qemuAddArgs
87
+ update configuration of vm with config
88
+
89
+ -- Get the current directory shares in registry
90
+ set reg to registry of vm
91
+ -- Add new directory shares to the registry
92
+ set reg to reg & directoryList
93
+ -- Update registry of vm with new directory shares
94
+ update registry of vm with reg
95
+ end tell
96
+ end run
@@ -0,0 +1,43 @@
1
+ ---
2
+ -- add_qemu_additional_args.applescript
3
+ -- This script adds qemu arguments to a specified UTM virtual machine.
4
+ -- Usage: osascript add_qemu_additional_args.applescript <VM_UUID> --args <arg1> <arg2> ...
5
+ -- Example: osascript add_qemu_additional_args.applescript A123 --args "-vnc 127.0.0.1:13" "-vnc..."
6
+
7
+ on run argv
8
+ set vmId to item 1 of argv # UUID of the VM
9
+
10
+ -- Initialize variables
11
+ set argsList to {}
12
+ set argsFlag to false
13
+
14
+ -- Parse the --args arguments
15
+ repeat with i from 2 to (count of argv)
16
+ set currentArg to item i of argv
17
+ if currentArg is "--args" then
18
+ set argsFlag to true
19
+ else if argsFlag then
20
+ set end of argsList to currentArg
21
+ end if
22
+ end repeat
23
+
24
+ tell application "UTM"
25
+ -- Get the VM and its configuration
26
+ set vm to virtual machine id vmId -- Id is assumed to be valid
27
+ set config to configuration of vm
28
+
29
+ -- Existing arguments
30
+ set qemuAddArgs to qemu additional arguments of config
31
+
32
+ -- Create new arguments from argsList and add them to the existing arguments
33
+ repeat with arg in argsList
34
+ set end of qemuAddArgs to {argument string:arg}
35
+ end repeat
36
+
37
+ --- set qemu args with new args list
38
+ set qemu additional arguments of config to qemuAddArgs
39
+
40
+ --- save the configuration (VM must be stopped)
41
+ update configuration of vm with config
42
+ end tell
43
+ end run
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Reads the shared directory IDs from the QEMU additional arguments of a specified VM in UTM.
3
+ *
4
+ * This function uses the UTM application to read the QEMU additional arguments
5
+ * of a VM identified by the given VM ID and extracts the IDs of shared directories.
6
+ *
7
+ * @param {string} vmIdentifier - The ID of the VM.
8
+ * @returns {string} A JSON string containing the shared directory IDs or an error message.
9
+ */
10
+ function run(argv) {
11
+ // Check if a VM ID is provided
12
+ if (argv.length === 0) {
13
+ console.log("Usage: osascript -l JavaScript read_shared_directories.js <vm_id>");
14
+ return JSON.stringify({ status: false, result: "No VM ID provided." });
15
+ }
16
+
17
+ const vmIdentifier = argv[0];
18
+ const utm = Application('UTM');
19
+ utm.includeStandardAdditions = true;
20
+
21
+ try {
22
+ // Attempt to get the VM by ID
23
+ const vm = utm.virtualMachines.byId(vmIdentifier);
24
+ // Get the config of the VM
25
+ const config = vm.configuration();
26
+ // Get the QEMU additional arguments
27
+ const qemuArgs = config.qemuAdditionalArguments;
28
+
29
+ // Extract shared directory IDs
30
+ const sharedDirIds = [];
31
+ qemuArgs.forEach(arg => {
32
+ const argStr = arg.argumentString;
33
+ if (argStr.startsWith("-fsdev")) {
34
+ const match = argStr.match(/id=([^,]+)/);
35
+ if (match) {
36
+ sharedDirIds.push(match[1]);
37
+ }
38
+ }
39
+ });
40
+
41
+ // Return the shared directory IDs
42
+ return JSON.stringify({ status: true, result: sharedDirIds });
43
+ } catch (error) {
44
+ // Return an error message
45
+ return JSON.stringify({ status: false, result: error.message });
46
+ }
47
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Reads the shared folder QEMU arguments for specified IDs from the QEMU additional arguments of a VM in UTM.
3
+ *
4
+ * This function uses the UTM application to read the QEMU additional arguments
5
+ * of a VM identified by the given VM ID and extracts the arguments for the specified IDs.
6
+ *
7
+ * @param {string} vmIdentifier - The ID of the VM.
8
+ * @param {string} ids - Comma-separated list of directory IDs.
9
+ * @returns {string} A JSON string containing the QEMU arguments for the specified IDs or an error message.
10
+ */
11
+ function run(argv) {
12
+ // Check if a VM ID and IDs are provided
13
+ if (argv.length < 2) {
14
+ console.log("Usage: osascript -l JavaScript read_shared_folders_args.js <vm_id> --ids <dirID>,<dirID>");
15
+ return JSON.stringify({ status: false, result: "No VM ID or IDs provided." });
16
+ }
17
+
18
+ const vmIdentifier = argv[0];
19
+ const idsArgIndex = argv.indexOf("--ids");
20
+ if (idsArgIndex === -1 || idsArgIndex + 1 >= argv.length) {
21
+ return JSON.stringify({ status: false, result: "No IDs provided." });
22
+ }
23
+ const ids = argv[idsArgIndex + 1].split(",");
24
+
25
+ const utm = Application('UTM');
26
+ utm.includeStandardAdditions = true;
27
+
28
+ try {
29
+ // Attempt to get the VM by ID
30
+ const vm = utm.virtualMachines.byId(vmIdentifier);
31
+ // Get the config of the VM
32
+ const config = vm.configuration();
33
+ // Get the QEMU additional arguments
34
+ const qemuArgs = config.qemuAdditionalArguments;
35
+
36
+ // Extract QEMU arguments for the specified IDs
37
+ const sharedDirArgs = [];
38
+ qemuArgs.forEach(arg => {
39
+ const argStr = arg.argumentString;
40
+ ids.forEach(id => {
41
+ if (argStr.includes(`id=${id}`) || argStr.includes(`fsdev=${id}`)) {
42
+ sharedDirArgs.push(argStr);
43
+ }
44
+ });
45
+ });
46
+
47
+ // Return the QEMU arguments for the specified IDs
48
+ return JSON.stringify({ status: true, result: sharedDirArgs });
49
+ } catch (error) {
50
+ // Return an error message
51
+ return JSON.stringify({ status: false, result: error.message });
52
+ }
53
+ }
@@ -0,0 +1,46 @@
1
+ ---
2
+ -- remove_qemu_additional_args.applescript
3
+ -- This script removes specified qemu arguments from a specified UTM virtual machine.
4
+ -- Usage: osascript remove_qemu_additional_args.applescript <VM_UUID> --args <arg1> <arg2> ...
5
+ -- Example: osascript remove_qemu_additional_args.applescript A123 --args "-vnc 127.0.0.1:13" "-vnc..."
6
+
7
+ on run argv
8
+ set vmId to item 1 of argv -- UUID of the VM
9
+
10
+ -- Initialize variables
11
+ set argsToRemove to {}
12
+ set argsFlag to false
13
+
14
+ -- Parse the --args arguments
15
+ repeat with i from 2 to (count of argv)
16
+ set currentArg to item i of argv
17
+ if currentArg is "--args" then
18
+ set argsFlag to true
19
+ else if argsFlag then
20
+ set end of argsToRemove to currentArg
21
+ end if
22
+ end repeat
23
+
24
+ tell application "UTM"
25
+ -- Get the VM and its configuration
26
+ set vm to virtual machine id vmId -- Id is assumed to be valid
27
+ set config to configuration of vm
28
+
29
+ -- Get the current QEMU additional arguments
30
+ set qemuAddArgs to qemu additional arguments of config
31
+
32
+ -- Initialize a new list for the updated arguments
33
+ set updatedArgs to {}
34
+
35
+ -- Iterate through the current arguments and add all except the ones to remove
36
+ repeat with arg in qemuAddArgs
37
+ if arg is not in argsToRemove then
38
+ set end of updatedArgs to arg
39
+ end if
40
+ end repeat
41
+
42
+ -- Update the configuration with the new arguments list
43
+ set qemu additional arguments of config to updatedArgs
44
+ update configuration of vm with config
45
+ end tell
46
+ end run
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module Utm
5
+ # Default Synced folder implementation for UTM
6
+ class SyncedFolder < Vagrant.plugin("2", :synced_folder)
7
+ def usable?(machine, _raise_errors = false) # rubocop:disable Style/OptionalBooleanParameter
8
+ # These synced folders only work if the provider is UTM
9
+ return false if machine.provider_name != :utm
10
+
11
+ # This only happens with `vagrant package --base`. Sigh.
12
+ return true unless machine.provider_config
13
+
14
+ machine.provider_config.functional_9pfs
15
+ end
16
+
17
+ # This is called before VM Boot to prepare the synced folders.
18
+ # Add required configs to the VM.
19
+ def prepare(machine, folders, _opts)
20
+ share_folders(machine, folders)
21
+ end
22
+
23
+ # This is called after VM Boot to mount the synced folders.
24
+ # Mount the shared folders inside the VM.
25
+ def enable(machine, folders, _opts) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/PerceivedComplexity
26
+ share_folders(machine, folders)
27
+
28
+ # sort guestpaths first, so we don't step on ourselves
29
+ folders = folders.sort_by do |_id, data|
30
+ if data[:guestpath]
31
+ data[:guestpath].length
32
+ else
33
+ # A long enough path to just do this at the end.
34
+ 10_000
35
+ end
36
+ end
37
+
38
+ # Go through each folder and mount
39
+ machine.ui.output(I18n.t("vagrant.actions.vm.share_folders.mounting"))
40
+ # refresh fstab
41
+ fstab_folders = [] # rubocop:disable Lint/UselessAssignment
42
+ folders.each do |id, data|
43
+ if data[:guestpath]
44
+ # Guest path specified, so mount the folder to specified point
45
+ machine.ui.detail(I18n.t("vagrant.actions.vm.share_folders.mounting_entry",
46
+ guestpath: data[:guestpath],
47
+ hostpath: data[:hostpath]))
48
+
49
+ # Dup the data so we can pass it to the guest API
50
+ data = data.dup
51
+
52
+ # Calculate the owner and group
53
+ ssh_info = machine.ssh_info
54
+ data[:owner] ||= ssh_info[:username]
55
+ data[:group] ||= ssh_info[:username]
56
+
57
+ # Unmount the folder before we mount it
58
+ machine.guest.capability(
59
+ :unmount_virtualbox_shared_folder,
60
+ data[:guestpath], data
61
+ )
62
+
63
+ # Mount the actual folder
64
+ machine.guest.capability(
65
+ :mount_virtualbox_shared_folder,
66
+ os_friendly_id(id), data[:guestpath], data
67
+ )
68
+ else
69
+ # If no guest path is specified, then automounting is disabled
70
+ machine.ui.detail(I18n.t("vagrant.actions.vm.share_folders.nomount_entry",
71
+ hostpath: data[:hostpath]))
72
+ end
73
+ end
74
+ end
75
+
76
+ def disable(machine, folders, _opts)
77
+ if machine.guest.capability?(:unmount_virtualbox_shared_folder)
78
+ folders.each_value do |data|
79
+ machine.guest.capability(
80
+ :unmount_virtualbox_shared_folder,
81
+ data[:guestpath], data
82
+ )
83
+ end
84
+ end
85
+
86
+ # Remove the shared folders from the VM metadata
87
+ names = folders.map { |id, _data| os_friendly_id(id) }
88
+ driver(machine).unshare_folders(names)
89
+ end
90
+
91
+ def cleanup(machine, _opts)
92
+ driver(machine).clear_shared_folders if machine.id && machine.id != ""
93
+ end
94
+
95
+ protected
96
+
97
+ # This is here so that we can stub it for tests
98
+ def driver(machine)
99
+ machine.provider.driver
100
+ end
101
+
102
+ def os_friendly_id(id)
103
+ id.gsub(%r{[\s/\\]}, "_").sub(/^_/, "")
104
+ end
105
+
106
+ # share_folders sets up the shared folder definitions on the
107
+ # UTM VM.
108
+ #
109
+ def share_folders(machine, folders)
110
+ defs = []
111
+
112
+ folders.each do |id, data|
113
+ hostpath = data[:hostpath]
114
+ hostpath = Vagrant::Util::Platform.cygwin_windows_path(hostpath) unless data[:hostpath_exact]
115
+
116
+ defs << {
117
+ name: os_friendly_id(id),
118
+ hostpath: hostpath.to_s,
119
+ automount: !data[:automount].nil?
120
+ }
121
+ end
122
+
123
+ driver(machine).share_folders(defs)
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copied from vagrant/plugins/synced_folder/unix_mount_helpers.rb
4
+ # Copyright (c) HashiCorp, Inc.
5
+ # SPDX-License-Identifier: BUSL-1.1
6
+
7
+ require "shellwords"
8
+ require "vagrant/util/retryable"
9
+
10
+ module VagrantPlugins
11
+ module SyncedFolder
12
+ # Contains helper methods for mounting folders on Unix-based systems.
13
+ module UnixMountHelpers # rubocop:disable Metrics/ModuleLength
14
+ def self.extended(klass)
15
+ unless klass.class_variable_defined?(:@@logger)
16
+ klass.class_variable_set(:@@logger, Log4r::Logger.new(klass.name.downcase)) # rubocop:disable Style/ClassVars
17
+ end
18
+ klass.extend Vagrant::Util::Retryable
19
+ end
20
+
21
+ def detect_owner_group_ids(machine, guest_path, mount_options, options) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
22
+ mount_uid = find_mount_options_id("uid", mount_options)
23
+ mount_gid = find_mount_options_id("gid", mount_options)
24
+
25
+ if mount_uid.nil?
26
+ if options[:owner].to_i.to_s == options[:owner].to_s
27
+ mount_uid = options[:owner]
28
+ class_variable_get(:@@logger).debug("Owner user ID (provided): #{mount_uid}")
29
+ else
30
+ output = { stdout: String.new, stderr: String.new } # Ensure strings are not frozen
31
+ uid_command = "id -u #{options[:owner]}"
32
+ machine.communicate.execute(uid_command,
33
+ error_class: Vagrant::Errors::VirtualBoxMountFailed,
34
+ error_key: :virtualbox_mount_failed,
35
+ command: uid_command,
36
+ output: output[:stderr]) { |type, data| output[type] << data if output[type] }
37
+ mount_uid = output[:stdout].chomp
38
+ class_variable_get(:@@logger).debug("Owner user ID (lookup): #{options[:owner]} -> #{mount_uid}")
39
+ end
40
+ else
41
+ machine.ui.warn "Detected mount owner ID within mount options. (uid: #{mount_uid} guestpath: #{guest_path})"
42
+ end
43
+
44
+ if mount_gid.nil?
45
+ if options[:group].to_i.to_s == options[:group].to_s
46
+ mount_gid = options[:group]
47
+ class_variable_get(:@@logger).debug("Owner group ID (provided): #{mount_gid}")
48
+ else
49
+ begin
50
+ { stdout: String.new, stderr: String.new } # Ensure strings are not frozen
51
+ gid_command = "getent group #{options[:group]}"
52
+ machine.communicate.execute(gid_command,
53
+ error_class: Vagrant::Errors::VirtualBoxMountFailed,
54
+ error_key: :virtualbox_mount_failed,
55
+ command: gid_command,
56
+ output: output[:stderr]) { |type, data| output[type] << data if output[type] }
57
+ mount_gid = output[:stdout].split(":").at(2).to_s.chomp
58
+ class_variable_get(:@@logger).debug("Owner group ID (lookup): #{options[:group]} -> #{mount_gid}")
59
+ rescue Vagrant::Errors::VirtualBoxMountFailed
60
+ if options[:owner] == options[:group] # rubocop:disable Metrics/BlockNesting
61
+ class_variable_get(:@@logger).debug("Failed to locate group `#{options[:group]}`. Group name matches owner. Fetching effective group ID.") # rubocop:disable Layout/LineLength
62
+ output = { stdout: String.new }
63
+ result = machine.communicate.execute("id -g #{options[:owner]}",
64
+ error_check: false) do |type, data|
65
+ output[type] << data if output[type] # rubocop:disable Metrics/BlockNesting
66
+ end
67
+ mount_gid = output[:stdout].chomp if result.zero? # rubocop:disable Metrics/BlockNesting
68
+ class_variable_get(:@@logger).debug("Owner group ID (effective): #{mount_gid}")
69
+ end
70
+ raise unless mount_gid
71
+ end
72
+ end
73
+ else
74
+ machine.ui.warn "Detected mount group ID within mount options. (gid: #{mount_gid} guestpath: #{guest_path})"
75
+ end
76
+ { gid: mount_gid, uid: mount_uid }
77
+ end
78
+
79
+ def find_mount_options_id(id_name, mount_options) # rubocop:disable Metrics/AbcSize
80
+ id_line = mount_options.detect { |line| line.include?("#{id_name}=") }
81
+ if id_line
82
+ match = id_line.match(/,?#{Regexp.escape(id_name)}=(?<option_id>\d+),?/)
83
+ found_id = match["option_id"]
84
+ updated_id_line = [
85
+ match.pre_match,
86
+ match.post_match
87
+ ].find_all { |string| !string.empty? }.join(",")
88
+ if updated_id_line.empty?
89
+ mount_options.delete(id_line)
90
+ else
91
+ idx = mount_options.index(id_line)
92
+ mount_options.delete(idx)
93
+ mount_options.insert(idx, updated_id_line)
94
+ end
95
+ end
96
+ found_id
97
+ end
98
+
99
+ def emit_upstart_notification(machine, guest_path)
100
+ # Emit an upstart event if we can
101
+ machine.communicate.sudo <<-NOTIFICATION.gsub(/^ {12}/, "")
102
+ if test -x /sbin/initctl && command -v /sbin/init && /sbin/init 2>/dev/null --version | grep upstart; then
103
+ /sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{guest_path}
104
+ fi
105
+ NOTIFICATION
106
+ end
107
+
108
+ def merge_mount_options(base, overrides) # rubocop:disable Metrics/AbcSize
109
+ base = base.join(",").split(",")
110
+ overrides = overrides.join(",").split(",")
111
+ b_kv = Hash[base.map { |item| item.split("=", 2) }]
112
+ o_kv = Hash[overrides.map { |item| item.split("=", 2) }]
113
+ merged = {}.tap do |opts|
114
+ (b_kv.keys + o_kv.keys).uniq.each do |key|
115
+ opts[key] = o_kv.fetch(key, b_kv[key])
116
+ end
117
+ end
118
+ merged.map do |key, value|
119
+ [key, value].compact.join("=")
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -4,6 +4,6 @@ module VagrantPlugins
4
4
  # Top level module for the Utm provider plugin.
5
5
  module Utm
6
6
  # Current version of the Utm provider plugin.
7
- VERSION = "0.1.1"
7
+ VERSION = "0.1.3"
8
8
  end
9
9
  end