bwrap 1.1.1 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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