bwrap 1.1.0.pre.rc1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,19 +6,13 @@ require_relative "args"
6
6
  module Bwrap::Args::Mount
7
7
  # Arguments for readwrite-binding {Config#root} as /.
8
8
  private def root_mount
9
- return unless @config.root
9
+ return unless @config&.root
10
10
 
11
11
  debug "Binding #{@config.root} as /"
12
12
  @args.add :root_mount, "--bind", @config.root, "/"
13
13
  @args.add :root_mount, "--chdir", "/"
14
14
  end
15
15
 
16
- # Arguments for mounting devtmpfs to /dev.
17
- private def dev_mount
18
- debug "Mounting new devtmpfs to /dev"
19
- @args.add :dev_mounts, "--dev", "/dev"
20
- end
21
-
22
16
  # Arguments for mounting procfs to /proc.
23
17
  private def proc_mount
24
18
  debug "Mounting new procfs to /proc"
@@ -17,7 +17,7 @@ class Bwrap::Args::Network
17
17
 
18
18
  # Arguments to set hostname to whatever is configured.
19
19
  def hostname
20
- return unless @config.hostname
20
+ return unless @config&.hostname
21
21
 
22
22
  debug "Setting hostname to #{@config.hostname}"
23
23
  @args.add :hostname, %W{ --hostname #{@config.hostname} }
@@ -35,7 +35,7 @@ class Bwrap::Args::Network
35
35
 
36
36
  # Arguments to allow network connection inside sandbox.
37
37
  def share_net
38
- return unless @config.share_net
38
+ return unless @config&.share_net
39
39
 
40
40
  verb "Sharing network"
41
41
  @args.add :network, %w{ --share-net }
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bwrap/output"
4
+ require_relative "args"
5
+
6
+ # User related arguments.
7
+ class Bwrap::Args::User
8
+ include Bwrap::Output
9
+
10
+ # Instance of {Config}.
11
+ attr_writer :config
12
+
13
+ # @param args [Args] Args created by {Construct}
14
+ def initialize args
15
+ @args = args
16
+ end
17
+
18
+ # Arguments to create `/run/user/#{uid}`.
19
+ def create_user_dir
20
+ trace "Creating directory /run/user/#{uid}"
21
+ @args.add :user_dir, %W{ --dir /run/user/#{uid} }
22
+ end
23
+
24
+ # Arguments to bind necessary pulseaudio data for audio support.
25
+ def read_only_pulseaudio
26
+ return unless @config&.audio&.include? :pulseaudio
27
+
28
+ debug "Binding pulseaudio"
29
+ @args.add :audio, %W{ --ro-bind /run/user/#{uid}/pulse /run/user/#{uid}/pulse }
30
+ end
31
+
32
+ # Returns current user id.
33
+ private def uid
34
+ Process.uid
35
+ end
36
+ end
data/lib/bwrap/bwrap.rb CHANGED
@@ -12,14 +12,27 @@ require "bwrap/execution"
12
12
  # using given configuration.
13
13
  #
14
14
  # @see ::Bwrap Bwrap module for usage example
15
+ #
16
+ # TODO: Add some means to validate what a command would actually bind.
17
+ # Like, create a method like Bwrap#wrapped_command which would just return the command instead of executing it.
18
+ # Then add some instructions about this feature, as for security it is important to know what a thing actually does.
19
+ #
20
+ # TODO: There should be a proper shell feature, which binds some always necessary things for shells.
21
+ # Maybe by default only enough to run a command using a shell, i.e. running “true” would work,
22
+ # instead of needing to specify its path.
15
23
  class Bwrap::Bwrap
16
24
  include Bwrap::Execution
17
25
 
26
+ # @note When config is not given, alternative root directory can be specified
27
+ # with {#run_inside_root}.
28
+ #
18
29
  # @param config [Bwrap::Config] Configuration used to tailor bwrap
19
- def initialize config
30
+ def initialize config = nil
20
31
  # TODO: Ensure that costruct.rb and utilities it uses does not enforce Config to be valid object.
21
32
  # Create a test for that also. Well, as long as that makes sense. If it doesn’t work, validate
22
33
  # Config object to be valid here.
34
+ #
35
+ # Related to above, it is now optional, but a test still needs to be done.
23
36
  @config = config
24
37
  end
25
38
 
@@ -38,35 +51,56 @@ class Bwrap::Bwrap
38
51
  Bwrap::Output.handle_output_options options
39
52
  end
40
53
 
54
+ # @todo Proper documentation.
55
+ #
56
+ # @param command [String, Array] Command to be executed inside bwrap along with necessary arguments
57
+ # @param config [Bwrap::Config|nil] Configuration to tailor bwrap sandbox.
58
+ # Defaults to config given to {#initialize}
59
+ def build_bwrap_arguments command, config: nil
60
+ construct = Bwrap::Args::Construct.new
61
+ construct.command = command
62
+ construct.config = (config || @config)
63
+
64
+ construct.calculate
65
+ bwrap_args = construct.bwrap_arguments
66
+ @construct = construct
67
+
68
+ exec_command = [ "bwrap" ]
69
+ exec_command += bwrap_args
70
+ exec_command.append command
71
+ exec_command += @cli_args if @cli_args
72
+
73
+ exec_command
74
+ end
75
+
41
76
  # Binds and executes given command available on running system inside bwrap.
42
77
  #
43
78
  # If {Config#root} has been set, given executable is scanned for necessary
44
79
  # libraries and they are bound inside sandbox. This often reduces amount of
45
80
  # global binds, but some miscellaneous things may need custom handling.
46
81
  #
82
+ # `kwargs` are passed to {#execute}.
83
+ #
47
84
  # @see Config#features about binding bigger feature sets to sandbox.
48
85
  #
49
86
  # Executed command is constructed using configuration passed to constructor.
50
87
  # After execution has completed, bwrap will also shut down.
51
88
  #
52
89
  # @param command [String, Array] Command to be executed inside bwrap along with necessary arguments
90
+ # @option kwargs [Integer]
91
+ # log_callback (1)
92
+ # Semi-internal variable used to tailor caller in debug messages
53
93
  # @see #run_inside_root to execute a command that already is inside sandbox.
54
- def run command
55
- construct = Bwrap::Args::Construct.new
56
- construct.command = command
57
- construct.config = @config
58
-
59
- construct.calculate
60
- bwrap_args = construct.bwrap_arguments
94
+ def run command, **kwargs
95
+ exec_command = build_bwrap_arguments command
61
96
 
62
- exec_command = [ "bwrap" ]
63
- exec_command += bwrap_args
64
- exec_command.append command
65
- exec_command += @cli_args if @cli_args
97
+ # add 1 to log_callback so executing is shown to where this method is called at.
98
+ kwargs[:log_callback] ||= 1
99
+ kwargs[:log_callback] += 1
66
100
 
67
- execute exec_command
101
+ execute exec_command, **kwargs
68
102
 
69
- construct.cleanup
103
+ @construct.cleanup
70
104
  end
71
105
 
72
106
  # Convenience method to executes a command that is inside bwrap.
@@ -75,12 +109,14 @@ class Bwrap::Bwrap
75
109
  #
76
110
  # Calling this method is equivalent to setting {Config#command_inside_root} to `true`.
77
111
  #
112
+ # `kwargs` are passed to {#execute}.
113
+ #
78
114
  # @note This may have a bit unintuitive usage, as most things are checked anyway, so this is not
79
115
  # akin to running something inside a chroot, but rather for convenience.
80
116
  #
81
117
  # @param command [String, Array] Command to be executed inside bwrap along with necessary arguments
82
118
  # @see #run to execute a command that needs to be bound to the sandbox.
83
- def run_inside_root command
119
+ def run_inside_root command, **kwargs
84
120
  if @config
85
121
  config = @config.dup
86
122
  config.command_inside_root = true
@@ -88,21 +124,15 @@ class Bwrap::Bwrap
88
124
  config = nil
89
125
  end
90
126
 
91
- construct = Bwrap::Args::Construct.new
92
- construct.command = command
93
- construct.config = config
94
-
95
- construct.calculate
96
- bwrap_args = construct.bwrap_arguments
127
+ # add 1 to log_callback so executing is shown to where this method is called at.
128
+ kwargs[:log_callback] ||= 1
129
+ kwargs[:log_callback] += 1
97
130
 
98
- exec_command = [ "bwrap" ]
99
- exec_command += bwrap_args
100
- exec_command.append command
101
- exec_command += @cli_args if @cli_args
131
+ exec_command = build_bwrap_arguments command, config: config
102
132
 
103
- execute exec_command
133
+ execute exec_command, **kwargs
104
134
 
105
- construct.cleanup
135
+ @construct.cleanup
106
136
  end
107
137
 
108
138
  # Parses global bwrap flags using Optimist.
data/lib/bwrap/config.rb CHANGED
@@ -17,6 +17,15 @@ require_relative "config/features"
17
17
  # Note that all attributes also have writers, even though they are not documented.
18
18
  #
19
19
  # @todo Add some documentation about syntax where necessary, like for #binaries_from.
20
+ #
21
+ # TODO: I don’t remember if I made bash feature, but maybe it should be done.
22
+ # It should automatically bind /bin and /usr/bin, with a flag to bind relevant sbin dirs.
23
+ # That is because most scripts needs stuff from there. Maybe my fish profile would give some
24
+ # useful basic stuff?
25
+ #
26
+ # TODO: Maybe I should have something like second-level configuration in variable “advanced” or similar,
27
+ # which allows controlling features that more directly maps to bwrap cli args? That way it would be easier
28
+ # to use only high-level features.
20
29
  class Bwrap::Config
21
30
  # Array of audio schemes usable inside chroot.
22
31
  #
@@ -31,6 +40,17 @@ class Bwrap::Config
31
40
  # @return [Boolean] `true` if executed command is inside sandbox
32
41
  attr_accessor :command_inside_root
33
42
 
43
+ # @return [Boolean] `true` if dummy devtmpfs should be mounted inside sandbox
44
+ attr_accessor :dev_mount
45
+
46
+ # Additional executables to bind to target.
47
+ #
48
+ # TODO: Implement this paragraph:
49
+ # If an executable given here is found from directory given to
50
+ # {#binaries_from=}, it is not bound to target, but only
51
+ # dependent libraries.
52
+ #
53
+ # @return [#each] Array of executables to bind
34
54
  attr_accessor :extra_executables
35
55
 
36
56
  # TODO: IIRC this doesn’t match the reality any more. So write correct documentation.
@@ -44,6 +64,10 @@ class Bwrap::Config
44
64
  # @return [Boolean] true if Linux library loaders are mounted inside chroot
45
65
  attr_accessor :full_system_mounts
46
66
 
67
+ # If set to `true`, things like /dev/dri is bound to sandbox to enable usage
68
+ # of hardware video acceleration, for example.
69
+ attr_accessor :graphics_acceleration
70
+
47
71
  attr_accessor :hostname
48
72
 
49
73
  # Set to true if basic system directories, like /usr/lib and /usr/lib64,
@@ -54,6 +78,9 @@ class Bwrap::Config
54
78
  # Often it is enough to use {#full_system_mounts=} instead of binding all
55
79
  # system libraries using this flag.
56
80
  #
81
+ # It may also make sense to bind specific library directories
82
+ # using {#ro_binds=}.
83
+ #
57
84
  # @return [Boolean] true if libdirs are mounted to the chroot
58
85
  attr_accessor :libdir_mounts
59
86
 
@@ -89,11 +116,11 @@ class Bwrap::Config
89
116
  #
90
117
  # Given paths are also added to PATH environment variable inside sandbox.
91
118
  #
92
- # @hint At least on SUSE, many executables are symlinks to /etc/alternatives/*,
93
- # which in turn symlinks to versioned executable under the same bindir.
94
- # To use these executables, /etc/alternatives should also be bound:
119
+ # Hint: At least on SUSE, many executables are symlinks to /etc/alternatives/*,
120
+ # which in turn symlinks to versioned executable under the same bindir.
121
+ # To use these executables, /etc/alternatives should also be bound:
95
122
  #
96
- # config.ro_binds["/etc/alternatives"] = "/etc/alternatives"
123
+ # config.ro_binds["/etc/alternatives"] = "/etc/alternatives"
97
124
  #
98
125
  # @return [Array] Paths to directories where binaries are looked from.
99
126
  attr_reader :binaries_from
@@ -113,10 +140,10 @@ class Bwrap::Config
113
140
 
114
141
  # @overload ro_binds
115
142
  # `Hash`[`Pathname`] => `Pathname` containing custom read-only binds.
116
- # @overload ro_binds=
117
- # Set given hash of paths to be bound with --ro-bind.
143
+ # @overload ro_binds= binds
144
+ # Set given Hash of paths to be bound with --ro-bind.
118
145
  #
119
- # Key is source path, value is destination path.
146
+ # Key of `binds` is source path, value is destination path.
120
147
  #
121
148
  # Given source paths must exist.
122
149
  attr_reader :ro_binds
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "execute"
4
+
5
+ # Actually performs the execution.
6
+ class Bwrap::Execution::Exec
7
+ # @param value [Boolean] True if execution should be skipped
8
+ attr_writer :dry_run
9
+
10
+ attr_writer :clear_env
11
+ attr_writer :config
12
+ attr_writer :direct_output
13
+ attr_writer :env
14
+ attr_writer :error
15
+ attr_writer :fail
16
+ attr_writer :log
17
+ attr_writer :rootcmd
18
+ attr_writer :wait
19
+
20
+ attr_reader :last_status
21
+
22
+ # @see Bwrap::Execution.do_execute
23
+ def initialize command
24
+ @command = command
25
+
26
+ @fail = true
27
+ @wait = true
28
+ @log = true
29
+ @direct_output = false
30
+ @env = nil
31
+ @clear_env = false
32
+ @error = nil
33
+ @config = nil
34
+ @rootcmd = nil
35
+ end
36
+
37
+ def log_callback= value
38
+ # Adds one since this is another method in the chain.
39
+ @log_callback = value + 1
40
+ end
41
+
42
+ def execute
43
+ # Default to two steps back in history. Hopefully this is correct amount...
44
+ log_callback = @log_callback || 1
45
+
46
+ command = Bwrap::Execution::Execute.format_command @command, rootcmd: @rootcmd, config: @config
47
+ Bwrap::Execution::Logging.handle_logging command, log_callback: log_callback, log: @log, dry_run: @dry_run
48
+ return if @dry_run || Bwrap::Execution::Execute.dry_run
49
+
50
+ Bwrap::Execution::Execute.open_pipes @direct_output
51
+
52
+ # If command is an array, there can’t be arrays inside the array.
53
+ # For convenience, the array is flattened here, so callers can construct commands more easily.
54
+ if command.respond_to? :flatten!
55
+ command.flatten!
56
+ end
57
+
58
+ env = @env || {}
59
+
60
+ # If command is string, splat operator (the *) does not do anything. If array, it expand the arguments.
61
+ # This causes spawning work correctly, as that’s how spawn() expects to have the arguments.
62
+ pid = spawn(env, *command, err: [ :child, :out ], out: Bwrap::Execution::Execute.w, unsetenv_others: @clear_env)
63
+
64
+ output = Bwrap::Execution::Execute.finish_execution(log: @log, wait: @wait, direct_output: @direct_output)
65
+ return pid unless @wait
66
+
67
+ # This is instant return, but allows us to have $?/$CHILD_STATUS set.
68
+ Process.wait pid
69
+ @last_status = $CHILD_STATUS
70
+
71
+ output = Bwrap::Execution::Execute.process_output output: output
72
+ Bwrap::Execution::Execute.handle_execution_fail fail: @fail, error: @error, output: output, command: command
73
+
74
+ output
75
+ ensure
76
+ Bwrap::Execution::Execute.clean_variables
77
+ end
78
+ end
@@ -22,28 +22,27 @@ class Bwrap::Execution::Execute
22
22
  attr_accessor :dry_run
23
23
  end
24
24
 
25
- # Formats given command for logging, depending on dry-run flag.
26
- def self.handle_logging command, log_callback:, log:, dry_run:
27
- # The debug message contents will always be evaluated, so can just do it like this.
28
- log_command = calculate_log_command command
29
-
30
- if dry_run || Bwrap::Execution::Execute.dry_run
31
- puts "Would execute “#{log_command.force_encoding("UTF-8")}” at #{caller_locations(log_callback, 1)[0]}"
32
- return
25
+ # @param rootcmd [Array|Symbol|nil] Flags used to construct bubblewrap environment
26
+ # @param config [Bwrap::Config|nil] Configuration used to launch the command inside bubblewrap
27
+ # @return formatted command
28
+ def self.format_command command, rootcmd: nil, config: nil
29
+ # Flatten the command if required, so nils can be converted in more of cases.
30
+ # Flattenization is also done in executions, but they also take in account
31
+ # for example rootcmd, so they probably should be done in addition to this one.
32
+ if command.respond_to? :flatten!
33
+ command.flatten!
33
34
  end
34
35
 
35
- return unless log
36
+ prepare_for_execution command
36
37
 
37
- msg = "Executing “#{log_command.force_encoding("UTF-8")}” at #{caller_locations(log_callback, 1)[0]}"
38
- Bwrap::Output.debug_output msg
39
- end
40
-
41
- # @return formatted command.
42
- def self.format_command command, rootcmd:
43
- replace_nils command
44
- return command if rootcmd.nil?
45
-
46
- prepend_rootcmd command, rootcmd: rootcmd
38
+ if config
39
+ bwrap = Bwrap::Bwrap.new config
40
+ bwrap.build_bwrap_arguments command
41
+ elsif rootcmd
42
+ prepend_rootcmd command, rootcmd: rootcmd
43
+ else
44
+ command
45
+ end
47
46
  end
48
47
 
49
48
  # Opens pipes for command output handling.
@@ -73,7 +72,8 @@ class Bwrap::Execution::Execute
73
72
  exception = Bwrap::Execution::ExecutionFailed.new "Command execution failed",
74
73
  command: command,
75
74
  output: output
76
- raise exception, caller
75
+
76
+ raise exception, exception.message, caller
77
77
  end
78
78
 
79
79
  # @note It makes sense for caller to just return if wait has been set and not check output.
@@ -112,31 +112,20 @@ class Bwrap::Execution::Execute
112
112
  $CHILD_STATUS.success?
113
113
  end
114
114
 
115
- # Used by `#handle_logging`.
116
- private_class_method def self.calculate_log_command command
117
- return command.dup unless command.respond_to?(:join)
118
-
119
- temp = command.dup
120
-
121
- # Wrap multi word arguments to quotes, for convenience.
122
- # NOTE: This is not exactly safe, but this is only a debugging aid anyway.
123
- temp.map! do |argument|
124
- if argument.include? " "
125
- escaped = argument.gsub '"', '\"'
126
- argument = %["#{escaped}"]
127
- end
128
- argument
129
- end
130
-
131
- temp.join(" ")
132
- end
133
-
134
115
  # If command is an `Array`, Replaces nil values with "".
135
- private_class_method def self.replace_nils command
116
+ #
117
+ # Also converts Pathnames to strings.
118
+ private_class_method def self.prepare_for_execution command
136
119
  return unless command.respond_to? :map!
137
120
 
138
121
  command.map! do |argument|
139
- argument.nil? && "" || argument
122
+ if argument.is_a? Pathname
123
+ argument.to_s
124
+ elsif argument.nil?
125
+ ""
126
+ else
127
+ argument
128
+ end
140
129
  end
141
130
  end
142
131
 
@@ -3,8 +3,11 @@
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"
10
+ require_relative "popen2e"
8
11
 
9
12
  # Provides methods to execute commands and handle its output.
10
13
  #
@@ -35,47 +38,90 @@ module Bwrap::Execution
35
38
  #
36
39
  # fail == If true, an error is raised in case the command returns failure code.
37
40
  #
38
- # @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.
39
76
  #
40
77
  # @see #execute
41
- def self.do_execute command,
42
- fail: true,
43
- wait: true,
44
- log: true,
45
- direct_output: false,
46
- env: {},
47
- clear_env: false,
48
- error: nil,
49
- log_callback: 2,
50
- rootcmd: nil
51
- command = Execute.format_command command, rootcmd: rootcmd
52
- Execute.handle_logging command, log_callback: log_callback, log: log, dry_run: @dry_run
53
- 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
54
81
 
55
- 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
56
92
 
57
- # If command is an array, there can’t be arrays inside the array.
58
- # For convenience, the array is flattened here, so callers can construct commands more easily.
59
- if command.respond_to? :flatten!
60
- command.flatten!
93
+ unless kwargs.empty?
94
+ raise ArgumentError, %{unknown keywords: #{kwargs.keys.join ","}}
61
95
  end
62
96
 
63
- # If command is string, splat operator (the *) does not do anything. If array, it expand the arguments.
64
- # This causes spawning work correctly, as that’s how spawn() expects to have the arguments.
65
- pid = spawn(env, *command, err: [ :child, :out ], out: Execute.w, unsetenv_others: clear_env)
66
- output = Execute.finish_execution(log: log, wait: wait, direct_output: direct_output)
67
- return pid unless wait
68
-
69
- # This is instant return, but allows us to have $?/$CHILD_STATUS set.
70
- Process.wait pid
71
- @last_status = $CHILD_STATUS
97
+ executor.execute
98
+ ensure
99
+ @last_status = executor.last_status if executor&.last_status
100
+ end
72
101
 
73
- output = Execute.process_output output: output
74
- Execute.handle_execution_fail fail: fail, error: error, output: output, command: command
75
- output
102
+ # Works similarly to Ruby’s official `Open3.popen2e`.
103
+ #
104
+ # A block is accepted, as does `Open3.popen2e`. For now, at least.
105
+ #
106
+ # TODO: Verify default log_callback is correct
107
+ #
108
+ # @warning Only array style commands are accepted. For example, `ls /`
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)
76
121
  ensure
77
- Execute.clean_variables
122
+ @last_status = popen&.child_status
78
123
  end
124
+ module_function :popen2e
79
125
 
80
126
  # Returns Process::Status instance of last execution.
81
127
  #
@@ -91,13 +137,18 @@ module Bwrap::Execution
91
137
  # execute commands.
92
138
  #
93
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.
94
142
  private def execute *args, **kwargs
95
143
  # Mangle proper location to error message.
96
144
  if kwargs.is_a? Hash
97
- kwargs[:log_callback] = 3
145
+ kwargs[:log_callback] ||= 1
98
146
  else
99
- kwargs = { log_callback: 3 }
147
+ kwargs = { log_callback: 1 }
100
148
  end
149
+ # Add 1 to log callback so execution is shown to be from caller.
150
+ kwargs[:log_callback] += 1
151
+
101
152
  Bwrap::Execution.do_execute(*args, **kwargs)
102
153
  end
103
154
 
@@ -138,7 +189,7 @@ module Bwrap::Execution
138
189
  Bundler.with_unbundled_env do
139
190
  yield 2
140
191
  end
141
- elsif Bundler&.bundler_major_version == 1
192
+ elsif (Bundler&.bundler_major_version) == 1
142
193
  Bundler.with_clean_env do
143
194
  yield 1
144
195
  end