bwrap 1.1.1 → 1.3.1

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.
@@ -3,7 +3,9 @@
3
3
  require "bwrap/version"
4
4
  require "bwrap/output"
5
5
  require_relative "exceptions"
6
+ require_relative "exec"
6
7
  require_relative "execute"
8
+ require_relative "logging"
7
9
  require_relative "path"
8
10
  require_relative "popen2e"
9
11
 
@@ -36,68 +38,88 @@ module Bwrap::Execution
36
38
  #
37
39
  # fail == If true, an error is raised in case the command returns failure code.
38
40
  #
39
- # @param error if :show, warn()s output of the command is shown if execution failed.
41
+ # @option kwargs [Boolean]
42
+ # clear_env (false)
43
+ # If true, executed command will run with no environment variables
44
+ # @option kwargs [Bwrap::Config|nil]
45
+ # config (nil)
46
+ # Configuration used to launch the command inside bubblewrap
47
+ # @option kwargs [Boolean]
48
+ # direct_output (false)
49
+ # Instead of buffering output, it will be directly outputted to `$stdout`
50
+ # @option kwargs [Hash<String, String>]
51
+ # env ({})
52
+ # Environment variables to add to the command. Also see `clear_env` option
53
+ # @option kwargs [:show|nil]
54
+ # error (nil)
55
+ # if :show and execution failed, prints output of the command with {#warn}
56
+ # @option kwargs [Boolean]
57
+ # fail (true)
58
+ # If true, {ExecutionFailed} is raised if process did not finish successfully
59
+ # @option kwargs [Boolean]
60
+ # log (true)
61
+ # If true, prints output if verbose flag has been set.
62
+ # And if a log file has been configured, also logs to that file
63
+ # @option kwargs [Integer]
64
+ # log_callback (1)
65
+ # Semi-internal variable used to tailor caller in debug messages
66
+ # @option kwargs [Array|Symbol|nil]
67
+ # rootcmd (nil)
68
+ # Flags used to construct bubblewrap environment
69
+ # @option kwargs [Boolean]
70
+ # wait (true)
71
+ # If true, this method blocks until the command has finished.
72
+ # Otherwise returns immediately. It is possible for user to
73
+ # call `Process.wait` to wait for result at suitable place
74
+ #
75
+ # TODO: log kwarg could take log level as argument. Like :trace.
40
76
  #
41
77
  # @see #execute
42
- def self.do_execute command,
43
- fail: true,
44
- wait: true,
45
- log: true,
46
- direct_output: false,
47
- env: {},
48
- clear_env: false,
49
- error: nil,
50
- log_callback: 2,
51
- rootcmd: nil
52
- command = Execute.format_command command, rootcmd: rootcmd
53
- Execute.handle_logging command, log_callback: log_callback, log: log, dry_run: @dry_run
54
- return if @dry_run || Bwrap::Execution::Execute.dry_run
78
+ def self.do_execute command, **kwargs
79
+ executor = Exec.new command
80
+ executor.dry_run = @dry_run
55
81
 
56
- Execute.open_pipes direct_output
82
+ executor.clear_env = kwargs.key?(:clear_env) ? kwargs.delete(:clear_env) : false
83
+ executor.config = kwargs.key?(:config) ? kwargs.delete(:config) : nil
84
+ executor.direct_output = kwargs.key?(:direct_output) ? kwargs.delete(:direct_output) : false
85
+ executor.env = kwargs.key?(:env) ? kwargs.delete(:env) : {}
86
+ executor.error = kwargs.key?(:error) ? kwargs.delete(:error) : nil
87
+ executor.fail = kwargs.key?(:fail) ? kwargs.delete(:fail) : true
88
+ executor.log = kwargs.key?(:log) ? kwargs.delete(:log) : true
89
+ executor.log_callback = kwargs.key?(:log_callback) ? kwargs.delete(:log_callback) : 1
90
+ executor.rootcmd = kwargs.key?(:rootcmd) ? kwargs.delete(:rootcmd) : nil
91
+ executor.wait = kwargs.key?(:wait) ? kwargs.delete(:wait) : true
57
92
 
58
- # If command is an array, there can’t be arrays inside the array.
59
- # For convenience, the array is flattened here, so callers can construct commands more easily.
60
- if command.respond_to? :flatten!
61
- command.flatten!
93
+ unless kwargs.empty?
94
+ raise ArgumentError, %{unknown keywords: #{kwargs.keys.join ","}}
62
95
  end
63
96
 
64
- # If command is string, splat operator (the *) does not do anything. If array, it expand the arguments.
65
- # This causes spawning work correctly, as that’s how spawn() expects to have the arguments.
66
- pid = spawn(env, *command, err: [ :child, :out ], out: Execute.w, unsetenv_others: clear_env)
67
- output = Execute.finish_execution(log: log, wait: wait, direct_output: direct_output)
68
- return pid unless wait
69
-
70
- # This is instant return, but allows us to have $?/$CHILD_STATUS set.
71
- Process.wait pid
72
- @last_status = $CHILD_STATUS
73
-
74
- output = Execute.process_output output: output
75
- Execute.handle_execution_fail fail: fail, error: error, output: output, command: command
76
- output
97
+ executor.execute
77
98
  ensure
78
- Execute.clean_variables
99
+ @last_status = executor.last_status if executor&.last_status
79
100
  end
80
101
 
81
- # Works similarly to {Open3.popen2e}.
82
- #
83
- # TODO: If there will be any difference to input syntax, document those differences here.
84
- # For now, rootcmd option has been implemented.
102
+ # Works similarly to Ruby’s official `Open3.popen2e`.
85
103
  #
86
- # A block is accepted, as does {Open3.popen2e}. For now, at least.
87
- #
88
- # TODO: Implement this so that this uses same execution things as other things here.
89
- # This way bwrap actually can be integrated to this...
104
+ # A block is accepted, as does `Open3.popen2e`. For now, at least.
90
105
  #
91
106
  # TODO: Verify default log_callback is correct
92
107
  #
93
108
  # @warning Only array style commands are accepted. For example, `ls /`
94
- # is not ok, but `ls` or `%w{ ls / }` is ok.
95
- def popen2e *cmd, rootcmd: nil, log_callback: 1, log: true, &block
96
- popen = Bwrap::Execution::Popen2e.new
97
- popen.dry_run = @dry_run
98
- popen.popen2e(*cmd, rootcmd: rootcmd, log_callback: log_callback, log: log, &block)
109
+ # is not ok, but `ls` or `%w{ ls / }` is ok.
110
+ #
111
+ # @param config [Bwrap::Config] Configuration used to launch the executable inside bubblewrap
112
+ # @param rootcmd [Array|Symbol|nil] A strange way to use bwrap. Probably no sense to use this
113
+ # @param log [Boolean]
114
+ # If true, prints output if verbose flag has been set.
115
+ # And if a log file has been configured, also logs to that file
116
+ # @option log_callback [Integer] Semi-internal variable used to tailor caller in debug messages
117
+ def popen2e *cmd, rootcmd: nil, config: nil, log_callback: 2, log: true, &block
118
+ popen = Bwrap::Execution::Popen2e.new rootcmd: rootcmd, config: config
119
+ popen.dry_run = @dry_run # TODO: Add a test that tests that this works as it works with execute().
120
+ popen.popen2e(*cmd, log_callback: log_callback, log: log, &block)
99
121
  ensure
100
- @last_status = $CHILD_STATUS
122
+ @last_status = popen&.child_status
101
123
  end
102
124
  module_function :popen2e
103
125
 
@@ -115,13 +137,18 @@ module Bwrap::Execution
115
137
  # execute commands.
116
138
  #
117
139
  # @see .do_execute .do_execute for documentation of argument syntax
140
+ #
141
+ # TODO Default log_callback should be 2 or something, and callers should add one for each subsequent method.
118
142
  private def execute *args, **kwargs
119
143
  # Mangle proper location to error message.
120
144
  if kwargs.is_a? Hash
121
- kwargs[:log_callback] = 3
145
+ kwargs[:log_callback] ||= 1
122
146
  else
123
- kwargs = { log_callback: 3 }
147
+ kwargs = { log_callback: 1 }
124
148
  end
149
+ # Add 1 to log callback so execution is shown to be from caller.
150
+ kwargs[:log_callback] += 1
151
+
125
152
  Bwrap::Execution.do_execute(*args, **kwargs)
126
153
  end
127
154
 
@@ -162,7 +189,7 @@ module Bwrap::Execution
162
189
  Bundler.with_unbundled_env do
163
190
  yield 2
164
191
  end
165
- elsif Bundler&.bundler_major_version == 1
192
+ elsif (Bundler&.bundler_major_version) == 1
166
193
  Bundler.with_clean_env do
167
194
  yield 1
168
195
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: false
2
+
3
+ # force_encoding modifies string, so can’t freeze strings.
4
+
5
+ require "bwrap/output"
6
+ require_relative "execute"
7
+
8
+ # Logging utilities for execution.
9
+ class Bwrap::Execution::Logging
10
+ # Formats given command for logging, depending on dry-run flag.
11
+ def self.handle_logging command, log_callback:, log:, dry_run:
12
+ log_callback += 1 # This is another method in the chain, so need to add one.
13
+
14
+ # The debug message contents will always be evaluated, so can just do it like this.
15
+ log_command = calculate_log_command command
16
+
17
+ if dry_run || Bwrap::Execution::Execute.dry_run
18
+ puts "Would execute “#{log_command.force_encoding("UTF-8")}” at #{caller_locations(log_callback, 1)[0]}"
19
+ return
20
+ end
21
+
22
+ return unless log
23
+
24
+ msg = "Executing “#{log_command.force_encoding("UTF-8")}” at #{caller_locations(log_callback, 1)[0]}"
25
+ Bwrap::Output.debug_output msg
26
+ end
27
+
28
+ private_class_method def self.calculate_log_command command
29
+ return command.dup unless command.respond_to?(:join)
30
+
31
+ temp = command.dup
32
+
33
+ # NOTE: These are not exactly safe, but this is only a debugging aid anyway.
34
+ temp.map! do |argument|
35
+ if argument.empty?
36
+ # If empty value, insert it to quotes so pasting to cli is safer.
37
+ argument = %{'#{argument}'}
38
+ elsif argument.include? " "
39
+ # Wrap multi word arguments to quotes, for convenience.
40
+ escaped = argument.gsub '"', '\"'
41
+ argument = %{"#{escaped}"}
42
+ end
43
+ argument
44
+ end
45
+
46
+ temp.join(" ")
47
+ end
48
+ end
49
+
@@ -1,26 +1,83 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # @see {Bwrap::Execution.popen2e}
3
+ require "bwrap"
4
+ require "bwrap/output"
5
+ require_relative "logging"
6
+
7
+ # @see Bwrap::Execution.popen2e
4
8
  class Bwrap::Execution::Popen2e
9
+ include Bwrap::Output
10
+
5
11
  attr_writer :dry_run
6
12
 
7
- # @see {Bwrap::Execution.popen2e}
8
- # TODO: Add a test for this (does Ruby have anything I could use here?).
13
+ # @param rootcmd [Array|Symbol] Flags used to construct bubblewrap environment
14
+ # @param config [Bwrap::Config] Configuration used to launch the command inside bubblewrap
15
+ def initialize rootcmd: nil, config: nil
16
+ self.rootcmd = rootcmd
17
+ self.config = config
18
+ end
19
+
20
+ # @param value [Bwrap::Config] Configuration used to launch the command inside bubblewrap
21
+ def config= value
22
+ @config = value
23
+
24
+ return unless @config and @rootcmd
25
+
26
+ error "Only either rootcmd or config object can be given to popen2e. " \
27
+ "Please set rootcmd to nil if wanting to use config object."
28
+ end
29
+
30
+ # Can be used to implement kind of tag based bubblewrap command things.
31
+ # This system is not well documented, and originally done for purposes
32
+ # of the code where this gem was splitted from.
33
+ #
34
+ # Probably using {#config=} instead makes more sense.
35
+ #
36
+ # @param value [Array|Symbol] Flags used to construct bubblewrap environment
37
+ def rootcmd= value
38
+ @rootcmd = value
39
+
40
+ return unless @config and @rootcmd
41
+
42
+ error "Only either rootcmd or config object can be given to popen2e. " \
43
+ "Please set config to nil if wanting to use rootcmd flags."
44
+ end
45
+
46
+ # @see Bwrap::Execution.popen2e
9
47
  #
10
- # @note Also options accepted by {Open3.popen2e} are accepted
48
+ # @note Also options accepted by upstream’s `Open3.popen2e` are accepted
11
49
  # here, in addition to those specified here.
12
- def popen2e *cmd, rootcmd: nil, log_callback: 1, log: true, &block
13
- @rootcmd = rootcmd
14
- @log_callback = log_callback + 1 # Passing to another method, so need to add one more.
15
- @log = log
50
+ #
51
+ # @option kwargs [Boolean]
52
+ # log (true)
53
+ # If true, prints output if verbose flag has been set.
54
+ # And if a log file has been configured, also logs to that file
55
+ # @option kwargs [Integer]
56
+ # log_callback (3)
57
+ # Semi-internal variable used to tailor caller in debug messages
58
+ #
59
+ # @yieldreturn In case a block is given, its return value is also returned by popen2e
60
+ # @return [Array<(IO, IO, Thread)>] Write and read `IO` and a wait thread
61
+ def popen2e *cmd, **kwargs, &block
62
+ @log = kwargs.key?(:log) ? kwargs.delete(:log) : true
63
+ @log_callback = kwargs.key?(:log_callback) ? kwargs.delete(:log_callback) : 1
64
+
65
+ @log_callback += 1 # Passing to another method, so need to add one more.
66
+
16
67
  self.opts_from_input = cmd
68
+ if kwargs
69
+ @opts ||= {}
70
+ @opts.merge! kwargs
71
+ end
17
72
 
18
73
  resolve_actual_command cmd
19
74
 
20
- return if @dry_run || Bwrap::Execution::Execute.dry_run
75
+ return if dry_run?
21
76
 
22
77
  open_pipes
23
- popen_run(@actual_command, [ @in_read, @out_write ], [ @in_write, @out_read ], &block)
78
+ child_io = [ @in_read, @out_write ]
79
+ parent_io = [ @in_write, @out_read ]
80
+ popen_run(@actual_command, child_io, parent_io, &block)
24
81
  ensure
25
82
  if block
26
83
  @in_read.close
@@ -30,6 +87,13 @@ class Bwrap::Execution::Popen2e
30
87
  end
31
88
  end
32
89
 
90
+ # Only contains `Process::Status` if no block has been passed to {#popen2e}.
91
+ #
92
+ # @return [Process::Status|nil] Exit status of the process
93
+ def child_status
94
+ @child_status
95
+ end
96
+
33
97
  # Sets @opts from `cmd` given to {#popen2e}, if any extra options have been given.
34
98
  private def opts_from_input= cmd
35
99
  @opts = cmd.last.is_a?(Hash) && cmd.pop.dup || {}
@@ -54,8 +118,11 @@ class Bwrap::Execution::Popen2e
54
118
  # Delete option hash.
55
119
  option_hash = temp_cmd.pop if temp_cmd.last.is_a? Hash
56
120
 
57
- temp_cmd = Bwrap::Execution::Execute.format_command temp_cmd, rootcmd: @rootcmd
58
- Bwrap::Execution::Execute.handle_logging temp_cmd, log_callback: @log_callback, log: @log, dry_run: @dry_run
121
+ temp_cmd = Bwrap::Execution::Execute.format_command temp_cmd, rootcmd: @rootcmd, config: @config
122
+ Bwrap::Execution::Logging.handle_logging temp_cmd,
123
+ log_callback: @log_callback,
124
+ log: @log,
125
+ dry_run: @dry_run
59
126
 
60
127
  temp_cmd.unshift env_hash if env_hash
61
128
  temp_cmd.push option_hash if option_hash
@@ -79,9 +146,14 @@ class Bwrap::Execution::Popen2e
79
146
  ensure
80
147
  parent_io.each(&:close)
81
148
  wait_thr.join
149
+ @child_status = wait_thr.value # Process::Status
82
150
  end
83
151
  end
84
152
 
85
153
  result
86
154
  end
155
+
156
+ private def dry_run?
157
+ @dry_run || Bwrap::Execution::Execute.dry_run
158
+ end
87
159
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "exceptions"
3
4
  require_relative "bwrap_module"
4
5
 
5
6
  # Declare Execution module here so Bwrap::Execution module is
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "resolvers"
4
+
5
+ # Resolves for example symlinks and such stuff.
6
+ #
7
+ # @api private
8
+ class Bwrap::Resolvers::Executable
9
+ # May be either relative or absolute path. If relative, the path may be invalid.
10
+ #
11
+ # @return [Pathname|nil] The executable, without arguments
12
+ attr_reader :executable
13
+
14
+ # If the executable is a symlink, contains each resolved symlink
15
+ # and the real target. Otherwise only contains the executable.
16
+ #
17
+ # @return [Hash<Pathname, :path|:symlink>|nil] Resolved paths to the command
18
+ attr_reader :executable_paths
19
+
20
+ # @param command [Array|String|nil] The command given to {Bwrap#run}.
21
+ def initialize command = nil
22
+ self.command = command if command
23
+ end
24
+
25
+ # The command given to {Bwrap#run}.
26
+ #
27
+ # @see Bwrap::Args::Construct#command=
28
+ def command= value
29
+ self.executable = executable_from_command value
30
+ @raw_command = value
31
+ end
32
+
33
+ # Resolves given executable and sets relevant data.
34
+ def executable= value
35
+ @executable = Pathname.new value
36
+
37
+ @executable_paths = { @executable => :path }
38
+
39
+ return unless @executable.symlink?
40
+
41
+ temp = @executable
42
+ while temp.symlink?
43
+ temp = temp.readlink
44
+ @executable_paths[temp] = :symlink
45
+ end
46
+ end
47
+
48
+ # Returns true if executable name parsed from command is absolute
49
+ # path, and not only the executable name.
50
+ #
51
+ # @return [Bool] true if path full path, false if it is only executable name
52
+ def absolute_path?
53
+ return false unless @executable
54
+
55
+ @executable.absolute?
56
+ end
57
+
58
+ private def executable_from_command command
59
+ if command.is_a? String
60
+ return command
61
+ end
62
+
63
+ # Array-like.
64
+ if command.respond_to? :at
65
+ return command.at(0)
66
+ end
67
+
68
+ raise "Can’t recognize type of given command. Type: #{command.class}"
69
+ end
70
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bwrap/execution"
4
+ require "bwrap/output"
5
+ require_relative "../library"
6
+
7
+ # Base definitions for library resolver subclasses.
8
+ class Bwrap::Resolvers::Library::Base
9
+ include Bwrap::Execution
10
+ include Bwrap::Output
11
+
12
+ private def convert_binary_paths binary_paths
13
+ case binary_paths
14
+ when String
15
+ [ binary_paths ]
16
+ when Pathname
17
+ [ binary_paths.to_s ]
18
+ else
19
+ binary_paths
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bwrap/execution"
4
+ require "bwrap/output"
5
+ require_relative "llvm_readelf"
6
+ require_relative "musl"
7
+ require_relative "../resolvers"
8
+
9
+ # See ../library.rb for class documentation.
10
+ class Bwrap::Resolvers::Library
11
+ include Bwrap::Execution
12
+ include Bwrap::Output
13
+
14
+ # NOTE: This caching can be made more efficient, but need to look at it later, what to do about it.
15
+ @@needed_libraries_cache ||= []
16
+
17
+ # Empties `@@needed_libraries_cache`.
18
+ def self.clear_needed_libraries_cache
19
+ @@needed_libraries_cache.clear
20
+ end
21
+
22
+ class << self
23
+ def needed_libraries_cache
24
+ @@needed_libraries_cache
25
+ end
26
+ end
27
+
28
+ # Otherwise similar to {#needed_libraries}, but checks used libc to handle musl executables.
29
+ #
30
+ # @param executable [String] Path to the executable to find dependencies for
31
+ # @return [Array] Libraries the executable needs, if any
32
+ def libraries_needed_by executable
33
+ trace "Finding libraries needed by #{executable}"
34
+
35
+ # %i == interpreter, the library used to load the executable by kernel.
36
+ # %F == Path to given file.
37
+ output_format = "%i::SEPARATOR::%F"
38
+ scanelf_command = %W{ scanelf --nobanner --quiet --format #{output_format} }
39
+ scanelf_command << executable
40
+
41
+ data = execvalue scanelf_command
42
+
43
+ # If data is empty, target probably is a script of some sort.
44
+ if data.empty?
45
+ return []
46
+ end
47
+
48
+ data = data.strip
49
+ interpreter, _executable_path = data.split "::SEPARATOR::"
50
+ interpreter = Pathname.new interpreter
51
+
52
+ if interpreter.basename.to_s[0..6] == "ld-musl"
53
+ trace "Resolved to musl interpreter: #{interpreter}"
54
+ musl_needed_libraries executable
55
+ else
56
+ trace "Defaulting to glibc interpreter: #{interpreter}"
57
+ # For glibc, scanelf can return full paths for us most of time.
58
+ needed_libraries executable
59
+ end
60
+ end
61
+
62
+ # @param binary_paths [String, Array] one or more paths to be resolved
63
+ def musl_needed_libraries binary_paths
64
+ musl = Musl.new
65
+ @needed_libraries = musl.needed_libraries binary_paths
66
+ end
67
+
68
+ # @param binary_paths [String, Array] one or more paths to be resolved
69
+ def needed_libraries binary_paths
70
+ llvm_readelf = LLVMReadelf.new
71
+ @needed_libraries = llvm_readelf.needed_libraries binary_paths
72
+ end
73
+ end
74
+ # class Library ended
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ # Used via {Bwrap::Resolvers::Library}.
6
+ #
7
+ # @api private
8
+ class Bwrap::Resolvers::Library::LLVMReadelf < Bwrap::Resolvers::Library::Base
9
+ # Resolve dependents using llvm-readelf.
10
+ #
11
+ # @param binary_paths [String, Array] one or more paths to be resolved
12
+ def needed_libraries binary_paths
13
+ raise ArgumentError, "binary_paths is nil, expected an Array" if binary_paths.nil?
14
+
15
+ trace "Finding libraries #{binary_paths} requires"
16
+ @needed_libraries = []
17
+
18
+ llvm_readelf_command = %w{ llvm-readelf --needed-libs }
19
+
20
+ binary_paths = convert_binary_paths binary_paths
21
+
22
+ # Check if the exe is already resolved.
23
+ binary_paths.delete_if do |binary_path|
24
+ Bwrap::Resolvers::Library.needed_libraries_cache.include? binary_path
25
+ end
26
+
27
+ return [] if binary_paths.empty?
28
+
29
+ data = execvalue(llvm_readelf_command + binary_paths)
30
+ parse_llvm_readelf_output binary_paths, data
31
+
32
+ @needed_libraries
33
+ end
34
+
35
+ # Parses output returned by llvm-readelf --needed-libraries
36
+ #
37
+ # Sets libraries to @needed_libraries variable.
38
+ private def parse_llvm_readelf_output binary_paths, data
39
+ iter = data.split("\n").each
40
+ source_binary = binary_paths.first
41
+ begin
42
+ while (line = iter.next)
43
+ source_binary = line[6..-1].strip if line[0..5] == "File: "
44
+ next unless line[0..16] == "NeededLibraries ["
45
+
46
+ while (library = iter.next)
47
+ library = library.strip
48
+ # End of library list for current NeededLibraries block.
49
+ break if library == "]"
50
+
51
+ library = convert_to_full_path source_binary, library
52
+
53
+ add_library_to_needed_libraries library
54
+ end
55
+ end
56
+ rescue StopIteration
57
+ # End of the iteration.
58
+ end
59
+ end
60
+
61
+ # NOTE: Maybe this could be in general file here, if needed elsewhere also?
62
+ # This also applies to some other methods here.
63
+ #
64
+ # Finds where given library would be loaded from.
65
+ #
66
+ # source_binary is used to to if it has RPATH set as third possibility.
67
+ # TODO: Implement above.
68
+ #
69
+ # @param source_binary [String] Executable or library which is loading the binary
70
+ private def convert_to_full_path source_binary, library
71
+ # TODO: Somewhere put a cleanup for this variable, so this is erased
72
+ # before application is run. That will save at least 200 kB of memory.
73
+ @@ld_so_cache ||= File.read("/etc/ld.so.cache", mode: "rb")
74
+
75
+ path_regex = %r{[\w\-/. ]{3,}}
76
+
77
+ check_next = false
78
+ @@ld_so_cache.scan(path_regex).each do |match|
79
+ if check_next
80
+ # TODO: Any sense checking for executable bit?
81
+ return match if File.exist? match
82
+
83
+ check_next = false
84
+ end
85
+
86
+ check_next = true if match == library
87
+ end
88
+
89
+ warn "Failed to resolve full path of library #{library}, needed by #{source_binary}."
90
+ library
91
+ end
92
+
93
+ # @param library [Array] library to add.
94
+ private def add_library_to_needed_libraries library
95
+ raise ArgumentError, "library is nil, expected a String" if library.nil?
96
+
97
+ return if @needed_libraries.include? library
98
+
99
+ # Probably no sense to log these unless tracing,
100
+ # as dependencies should work most of time.
101
+ #
102
+ # It also outputs tons of output for more complex programs.
103
+ trace "Binding #{library}"
104
+
105
+ @needed_libraries << library
106
+
107
+ # Also check if requisite libraries needs some libraries.
108
+ inner = Bwrap::Resolvers::Library.new
109
+ @needed_libraries |= inner.needed_libraries library
110
+
111
+ Bwrap::Resolvers::Library.needed_libraries_cache << library
112
+ end
113
+
114
+ # @param libraries [Array] libraries to add.
115
+ private def add_libraries_to_needed_libraries libraries
116
+ # Add to needed libraries if not already added.
117
+ (@needed_libraries & libraries).each do |library|
118
+ # Probably no sense to log these unless tracing,
119
+ # as dependencies should work most of time.
120
+ #
121
+ # It also outputs tons of output for more complex programs.
122
+ trace "Binding #{library}"
123
+ end
124
+
125
+ @needed_libraries |= libraries
126
+
127
+ # Also check if requisite libraries needs some libraries.
128
+ inner = Bwrap::Resolvers::Library.new
129
+ @needed_libraries |= inner.needed_libraries libraries
130
+
131
+ Bwrap::Resolvers::Library.needed_libraries_cache |= libraries
132
+ end
133
+ end