bwrap 1.1.1 → 1.2.0

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.
@@ -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,24 +22,10 @@ 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
33
- end
34
-
35
- return unless log
36
-
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:
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
43
29
  # Flatten the command if required, so nils can be converted in more of cases.
44
30
  # Flattenization is also done in executions, but they also take in account
45
31
  # for example rootcmd, so they probably should be done in addition to this one.
@@ -47,10 +33,16 @@ class Bwrap::Execution::Execute
47
33
  command.flatten!
48
34
  end
49
35
 
50
- replace_nils command
51
- return command if rootcmd.nil?
36
+ prepare_for_execution command
52
37
 
53
- 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
54
46
  end
55
47
 
56
48
  # Opens pipes for command output handling.
@@ -80,7 +72,8 @@ class Bwrap::Execution::Execute
80
72
  exception = Bwrap::Execution::ExecutionFailed.new "Command execution failed",
81
73
  command: command,
82
74
  output: output
83
- raise exception, caller
75
+
76
+ raise exception, exception.message, caller
84
77
  end
85
78
 
86
79
  # @note It makes sense for caller to just return if wait has been set and not check output.
@@ -119,31 +112,20 @@ class Bwrap::Execution::Execute
119
112
  $CHILD_STATUS.success?
120
113
  end
121
114
 
122
- # Used by `#handle_logging`.
123
- private_class_method def self.calculate_log_command command
124
- return command.dup unless command.respond_to?(:join)
125
-
126
- temp = command.dup
127
-
128
- # Wrap multi word arguments to quotes, for convenience.
129
- # NOTE: This is not exactly safe, but this is only a debugging aid anyway.
130
- temp.map! do |argument|
131
- if argument.include? " "
132
- escaped = argument.gsub '"', '\"'
133
- argument = %["#{escaped}"]
134
- end
135
- argument
136
- end
137
-
138
- temp.join(" ")
139
- end
140
-
141
115
  # If command is an `Array`, Replaces nil values with "".
142
- 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
143
119
  return unless command.respond_to? :map!
144
120
 
145
121
  command.map! do |argument|
146
- argument.nil? && "" || argument
122
+ if argument.is_a? Pathname
123
+ argument.to_s
124
+ elsif argument.nil?
125
+ ""
126
+ else
127
+ argument
128
+ end
147
129
  end
148
130
  end
149
131
 
@@ -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