bwrap 1.0.0.pre.alpha5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,7 +6,7 @@ require_relative "args"
6
6
 
7
7
  # Class to clean up namespace for implementation specific reasons.
8
8
  #
9
- # @api internal
9
+ # @api private
10
10
  class Bwrap::Args::Library
11
11
  include Bwrap::Execution
12
12
  include Bwrap::Output
@@ -14,6 +14,11 @@ class Bwrap::Args::Library
14
14
  # NOTE: This caching can be made more efficient, but need to look at it later, what to do about it.
15
15
  @@needed_libraries_cache ||= []
16
16
 
17
+ # Empties {@@needed_libraries_cache}.
18
+ def self.clear_needed_libraries_cache
19
+ @@needed_libraries_cache.clear
20
+ end
21
+
17
22
  # Otherwise similar to {#needed_libraries}, but checks used libc to handle musl executables.
18
23
  #
19
24
  # @param executable [String] Path to the executable to find dependencies for
@@ -99,17 +104,17 @@ class Bwrap::Args::Library
99
104
  binary_path, libraries_line = line.split "::SEPARATOR::"
100
105
  libraries = libraries_line.split ","
101
106
 
102
- @needed_libraries += libraries
107
+ (@needed_libraries & libraries).each do |library|
108
+ verb "Binding #{library} as dependency of #{binary_path}"
109
+ end
110
+
111
+ @needed_libraries |= libraries
103
112
 
104
113
  # Also check if requisite libraries needs some libraries.
105
114
  inner = Bwrap::Args::Library.new
106
- @needed_libraries += inner.needed_libraries libraries
115
+ @needed_libraries |= inner.needed_libraries libraries
107
116
 
108
- # Mark library cached only after its dependencies have been also handled.
109
- libraries.each do |library|
110
- verb "Binding #{library} as dependency of #{binary_path}"
111
- @@needed_libraries_cache << library
112
- end
117
+ @@needed_libraries_cache |= libraries
113
118
  end
114
119
 
115
120
  # Used by {#musl_needed_libraries}.
@@ -120,11 +125,13 @@ class Bwrap::Args::Library
120
125
  matches = library_data.match(/(.*) \(0x[0-9a-f]+\)/)
121
126
  library_path = matches[1]
122
127
 
123
- @needed_libraries << library_path
128
+ unless @needed_libraries.include? library_path
129
+ @needed_libraries << library_path
130
+ end
124
131
 
125
132
  # Also check if requisite libraries needs some libraries.
126
133
  inner = Bwrap::Args::Library.new
127
- @needed_libraries += inner.musl_needed_libraries library_path
134
+ @needed_libraries |= inner.musl_needed_libraries library_path
128
135
  end
129
136
  end
130
137
  # class Library ended
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "securerandom"
4
+ require "tempfile"
4
5
 
5
6
  require "bwrap/output"
6
7
  require_relative "args"
@@ -24,7 +25,7 @@ class Bwrap::Args::MachineId
24
25
  # Returning [] means that execute() will ignore this fully.
25
26
  # Nil would be converted to empty string, causing spawn() to pass it as argument, causing
26
27
  # bwrap to misbehave.
27
- return unless @config.machine_id
28
+ return unless @config&.machine_id
28
29
 
29
30
  machine_id = @config.machine_id
30
31
 
@@ -51,10 +52,10 @@ class Bwrap::Args::MachineId
51
52
  debug "Using random machine id as /etc/machine-id"
52
53
 
53
54
  @machine_id_file = Tempfile.new "bwrap-random_machine_id-", @config.tmpdir
54
- @machine_id_file.write SecureRandom.uuid.gsub("-", "")
55
+ @machine_id_file.write SecureRandom.uuid.tr("-", "")
55
56
  @machine_id_file.flush
56
57
 
57
- %W{ --ro-bind-data #{machine_id_file.fileno} /etc/machine-id }
58
+ %W{ --ro-bind-data #{@machine_id_file.fileno} /etc/machine-id }
58
59
  end
59
60
 
60
61
  # Uses `10000000000000000000000000000000` as machine id.
@@ -79,6 +80,8 @@ class Bwrap::Args::MachineId
79
80
  end
80
81
 
81
82
  # Uses file inside sandbox directory as machine id.
83
+ #
84
+ # TODO: I kind of want to deprecate this one. It may make sense, but eh... Let’s see.
82
85
  private def machine_id_inside_sandbox_dir sandbox_directory
83
86
  machine_id_file = "#{sandbox_directory}/machine-id"
84
87
 
@@ -10,6 +10,7 @@ module Bwrap::Args::Mount
10
10
 
11
11
  debug "Binding #{@config.root} as /"
12
12
  @args.append %W{ --bind #{@config.root} / }
13
+ @args.append %w{ --chdir / }
13
14
  end
14
15
 
15
16
  # Arguments for mounting devtmpfs to /dev.
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ #require "deep-cover" if ENV["DEEP_COVER"]
4
+
5
+ require_relative "bwrap_module"
6
+ require "bwrap/version"
7
+ require "bwrap/args/construct"
8
+ require "bwrap/config"
9
+ require "bwrap/execution"
10
+
11
+ # Executes given command under {https://github.com/containers/bubblewrap bwrap}
12
+ # using given configuration.
13
+ #
14
+ # @see ::Bwrap Bwrap module for usage example
15
+ class Bwrap::Bwrap
16
+ include Bwrap::Execution
17
+
18
+ # @param config [Bwrap::Config] Configuration used to tailor bwrap
19
+ def initialize config
20
+ # TODO: Ensure that costruct.rb and utilities it uses does not enforce Config to be valid object.
21
+ # Create a test for that also. Well, as long as that makes sense. If it doesn’t work, validate
22
+ # Config object to be valid here.
23
+ @config = config
24
+ end
25
+
26
+ # Parses command line arguments given to caller script.
27
+ #
28
+ # @note This method automatically sets output levels according parsed
29
+ # options. It is also possible to set the levels manually using
30
+ # {Bwrap::Output.handle_output_options}, for example with flags
31
+ # parsed with optparse’s `OptionParser`, found in Ruby’s standard
32
+ # library.
33
+ #
34
+ # @warning Requires optimist gem to be installed, which is not a dependency of this gem.
35
+ def parse_command_line_arguments
36
+ options = parse_options
37
+
38
+ Bwrap::Output.handle_output_options options
39
+ end
40
+
41
+ # Binds and executes given command available on running system inside bwrap.
42
+ #
43
+ # If {Config#root} has been set, given executable is scanned for necessary
44
+ # libraries and they are bound inside sandbox. This often reduces amount of
45
+ # global binds, but some miscellaneous things may need custom handling.
46
+ #
47
+ # @see Config#features about binding bigger feature sets to sandbox.
48
+ #
49
+ # Executed command is constructed using configuration passed to constructor.
50
+ # After execution has completed, bwrap will also shut down.
51
+ #
52
+ # @param command [String, Array] Command to be executed inside bwrap along with necessary arguments
53
+ # @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
+ bwrap_args = construct.construct_bwrap_args
59
+
60
+ exec_command = [ "bwrap" ]
61
+ exec_command += bwrap_args
62
+ exec_command.append command
63
+ exec_command += @cli_args if @cli_args
64
+
65
+ execute exec_command
66
+
67
+ construct.cleanup
68
+ end
69
+
70
+ # Convenience method to executes a command that is inside bwrap.
71
+ #
72
+ # Given command is expected to already be inside {Config#root}.
73
+ #
74
+ # Calling this method is equivalent to setting {Config#command_inside_root} to `true`.
75
+ #
76
+ # @note This may have a bit unintuitive usage, as most things are checked anyway, so this is not
77
+ # akin to running something inside a chroot, but rather for convenience.
78
+ #
79
+ # @param command [String, Array] Command to be executed inside bwrap along with necessary arguments
80
+ # @see #run to execute a command that needs to be bound to the sandbox.
81
+ def run_inside_root command
82
+ if @config
83
+ config = @config.dup
84
+ config.command_inside_root = true
85
+ else
86
+ config = nil
87
+ end
88
+
89
+ construct = Bwrap::Args::Construct.new
90
+ construct.command = command
91
+ construct.config = config
92
+ bwrap_args = construct.construct_bwrap_args
93
+
94
+ exec_command = [ "bwrap" ]
95
+ exec_command += bwrap_args
96
+ exec_command.append command
97
+ exec_command += @cli_args if @cli_args
98
+
99
+ execute exec_command
100
+
101
+ construct.cleanup
102
+ end
103
+
104
+ # Parses global bwrap flags using Optimist.
105
+ #
106
+ # Sets instance variable `@cli_args`.
107
+ #
108
+ # @warning Requires optimist gem to be installed, which is not a dependency of this gem.
109
+ #
110
+ # @api private
111
+ # @return [Hash] options parsed by `Optimist.options`
112
+ private def parse_options
113
+ begin
114
+ require "optimist"
115
+ rescue LoadError => e
116
+ puts "Failed to load optimist gem. In order to use Bwrap::Bwrap#parse_command_line_arguments, " \
117
+ "ensure optimist gem is present for example in your Gemfile."
118
+ puts
119
+ raise e
120
+ end
121
+
122
+ options = Optimist.options do
123
+ version ::Bwrap::VERSION
124
+
125
+ banner "Usage:"
126
+ banner " #{$PROGRAM_NAME} [global options]\n \n"
127
+ banner "Global options:"
128
+ opt :verbose,
129
+ "Show verbose output",
130
+ short: "v"
131
+ opt :debug,
132
+ "Show debug output (useful when debugging a problem)",
133
+ short: "d"
134
+ opt :trace,
135
+ "Show trace output (noisiest, probably not useful for most of time)",
136
+ short: :none
137
+ opt :version,
138
+ "Print version and exit",
139
+ short: "V"
140
+ opt :help,
141
+ "Show help message",
142
+ short: "h"
143
+
144
+ educate_on_error
145
+ end
146
+
147
+ @cli_args = ARGV.dup
148
+
149
+ options
150
+ end
151
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ruby-bwrap provides easy-to-use interface to run complex programs in sandboxes created with
4
+ # {https://github.com/containers/bubblewrap bubblewrap}.
5
+ #
6
+ # To run a program inside bubblewrap, a wrapper executable can be created. For example:
7
+ #
8
+ # require "bwrap"
9
+ #
10
+ # config = Bwrap::Config.new
11
+ # config.user = "dummy_user"
12
+ # config.full_system_mounts = true
13
+ # config.binaries_from = %w{
14
+ # /bin
15
+ # /usr/bin
16
+ # }
17
+ #
18
+ # bwrap = Bwrap::Bwrap.new config
19
+ # bwrap.parse_command_line_arguments
20
+ # bwrap.run "/bin/true"
21
+ #
22
+ # There also are few generic utilities, {Bwrap::Output} for handling output of scripts and
23
+ # {Bwrap::Execution} to run executables.
24
+ module Bwrap
25
+ # Empty module.
26
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Bwrap::Config
4
+ # Methods to enable or disable feature sets to control various aspects of sandboxing.
5
+ class Features
6
+ # @abstract
7
+ #
8
+ # Base of all features.
9
+ class Base
10
+ # @param features [Bwrap::Config::Features] Instance of features object in {Config}
11
+ def initialize features
12
+ @features = features
13
+ end
14
+
15
+ # Checks if the feature has been enabled.
16
+ #
17
+ # @return [Boolean] whether feature is enabled
18
+ def enabled?
19
+ @enabled
20
+ end
21
+
22
+ # Enable the feature.
23
+ def enable
24
+ @enabled = true
25
+ end
26
+
27
+ # Disable the feature.
28
+ def disable
29
+ @enabled = false
30
+ end
31
+ end
32
+
33
+ # Defines Bash feature set.
34
+ class Bash < Base
35
+ # Nya.
36
+ end
37
+
38
+ # Defines Nscd feature set.
39
+ #
40
+ # nscd is short of name service cache daemon. It may make sense to
41
+ # have this class under another name, but I don’t know how nscd specific
42
+ # this feature can be, so this name it is for now.
43
+ class Nscd < Base
44
+ # Nya.
45
+ end
46
+
47
+ # Defines Ruby feature set.
48
+ #
49
+ # Implies {Nscd} feature.
50
+ class Ruby < Base
51
+ # Extra libraries to be loaded from `RbConfig::CONFIG["rubyarchdir"]`.
52
+ #
53
+ # @note This is only required to be called if extra dependencies are necessary.
54
+ # For example, psych.so requires libyaml.so.
55
+ #
56
+ # @return [Array] list of needed libraries.
57
+ #
58
+ # @overload stdlib
59
+ # @overload stdlib=(libs)
60
+ attr_reader :stdlib
61
+
62
+ def initialize features
63
+ super features
64
+
65
+ @gem_env_paths = true
66
+ @stdlib = []
67
+ end
68
+
69
+ # @return true if bindirs from “gem environment” should be added to sandbox.
70
+ def gem_env_paths?
71
+ @gem_env_paths
72
+ end
73
+
74
+ # Enable Ruby feature set.
75
+ #
76
+ # Among others, binds `RbConfig::CONFIG["sitedir"]` so scripts works.
77
+ #
78
+ # @note This does not allow development headers needed for compilation for now.
79
+ # I’ll look at it after I have an use for it.
80
+ #
81
+ # @note Also enables {Nscd} feature.
82
+ def enable
83
+ super
84
+
85
+ @features.nscd.enable
86
+ end
87
+
88
+ # @see #stdlib
89
+ def stdlib= libs
90
+ # Just a little check to have error earlier.
91
+ libs.each do |lib|
92
+ unless File.exist? "#{RbConfig::CONFIG["rubyarchdir"]}/#{lib}.so"
93
+ raise "Library “#{lib}” passed to Bwrap::Config::Ruby.stdlib= does not exist."
94
+ end
95
+ end
96
+
97
+ @stdlib = libs
98
+ end
99
+ end
100
+
101
+ # @return [Bash] Instance of feature class for Bash
102
+ def bash
103
+ @bash ||= Bash.new self
104
+ end
105
+
106
+ # @return [Nscd] Instance of feature class for nscd
107
+ def nscd
108
+ @nscd ||= Nscd.new self
109
+ end
110
+
111
+ # @return [Ruby] Instance of feature class for Ruby
112
+ def ruby
113
+ @ruby ||= Ruby.new self
114
+ end
115
+ end
116
+ end
data/lib/bwrap/config.rb CHANGED
@@ -3,13 +3,60 @@
3
3
  require "bwrap/output"
4
4
  require "bwrap/version"
5
5
 
6
- # Represents configuration set by user for bwrap execution.
6
+ # Features classes, used through {Config#features}.
7
+ require_relative "config/features"
8
+
9
+ # Represents configuration used to tailor bwrap execution.
10
+ #
11
+ # @developer_note
12
+ # Logger methods (debug, warn and so on) can’t be used here as logger
13
+ # isn’t initialized before this class.
14
+ #
15
+ # For same reason, it’s not advised to execute anything here.
7
16
  #
8
- # Implementation NOTE: logger methods (debug, warn and so on) can’t be used here as logger
9
- # isn’t initialized before this class.
17
+ # Note that all attributes also have writers, even though they are not documented.
18
+ #
19
+ # @todo Add some documentation about syntax where necessary, like for #binaries_from.
10
20
  class Bwrap::Config
21
+ # Array of audio schemes usable inside chroot.
22
+ #
23
+ # Currently supports:
24
+ # - :pulseaudio
25
+ #
26
+ attr_accessor :audio
27
+
28
+ # Set to `true` if command given to {Bwrap::Bwrap#run} is expected to
29
+ # be inside sandbox, and not bound from host.
30
+ #
31
+ # @return [Boolean] `true` if executed command is inside sandbox
32
+ attr_accessor :command_inside_root
33
+
34
+ attr_accessor :extra_executables
35
+
36
+ # TODO: IIRC this doesn’t match the reality any more. So write correct documentation.
37
+ #
38
+ # Causes libraries required by the executable given to {Bwrap#run} to be
39
+ # mounted inside sandbox.
40
+ #
41
+ # Often it is enough to use this flag instead of binding all system libraries
42
+ # using {#libdir_mounts=}
43
+ #
44
+ # @return [Boolean] true if Linux library loaders are mounted inside chroot
45
+ attr_accessor :full_system_mounts
46
+
11
47
  attr_accessor :hostname
12
48
 
49
+ # Set to true if basic system directories, like /usr/lib and /usr/lib64,
50
+ # should be bound inside chroot.
51
+ #
52
+ # /usr/bin can be mounted using {Config#binaries_from=}.
53
+ #
54
+ # Often it is enough to use {#full_system_mounts=} instead of binding all
55
+ # system libraries using this flag.
56
+ #
57
+ # @return [Boolean] true if libdirs are mounted to the chroot
58
+ attr_accessor :libdir_mounts
59
+
13
60
  # What should be used as /etc/machine_id file.
14
61
  #
15
62
  # If not specified, no /etc/machine_id handling is done.
@@ -24,6 +71,9 @@ class Bwrap::Config
24
71
  # Given file as bound as /etc/machine_id.
25
72
  attr_accessor :machine_id
26
73
 
74
+ # @return [Boolean] true if network should be shared from host.
75
+ attr_accessor :share_net
76
+
27
77
  # Name of the user inside chroot.
28
78
  #
29
79
  # This is optional and defaults to no user.
@@ -35,34 +85,23 @@ class Bwrap::Config
35
85
  # @return [Boolean] Whether Xorg specific binds are used.
36
86
  attr_accessor :xorg_application
37
87
 
38
- # Array of audio schemes usable inside chroot.
88
+ # Array of directories to be bind mounted in sandbox.
39
89
  #
40
- # Currently supports:
41
- # - :pulseaudio
90
+ # Given paths are also added to PATH environment variable inside sandbox.
42
91
  #
43
- attr_accessor :audio
44
-
45
- # @return [Boolean] true if network should be shared from host.
46
- attr_accessor :share_net
47
-
48
- # TODO: This should cause Bind#full_system_mounts to mount /lib64/ld-linux-x86-64.so.2 and so on,
49
- # probably according executable type of specified command. But that needs some magic.
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:
50
95
  #
51
- # For now this just mounts all relevant files it can find.
96
+ # config.ro_binds["/etc/alternatives"] = "/etc/alternatives"
52
97
  #
53
- # @return [Boolean] true if Linux library loaders are mounted inside chroot
54
- attr_accessor :full_system_mounts
98
+ # @return [Array] Paths to directories where binaries are looked from.
99
+ attr_reader :binaries_from
55
100
 
56
- # Set to true if basic system directories, like /usr/lib and /usr/lib64,
57
- # should be bound inside chroot.
101
+ # Paths to be added to sandbox instance’s PATH environment variable.
58
102
  #
59
- # /usr/bin can be mounted using {Config#binaries_from=}.
60
- #
61
- # @return [Boolean] true if libdirs are mounted to the chroot
62
- attr_accessor :libdir_mounts
63
-
64
- # Array of directories to be bind mounted and used to construct PATH environment variable.
65
- attr_reader :binaries_from
103
+ # @see #add_env_path
104
+ attr_reader :env_paths
66
105
 
67
106
  # TODO: Document this.
68
107
  # TODO: I wonder if this should just be removed. I don’t know, this is a bit ...
@@ -72,71 +111,34 @@ class Bwrap::Config
72
111
  # Use given directory as root. End result is similar to classic chroot.
73
112
  attr_reader :root
74
113
 
75
- # `Hash`[`Pathname`] => `Pathname` containing custom read-only binds.
114
+ # @overload ro_binds
115
+ # `Hash`[`Pathname`] => `Pathname` containing custom read-only binds.
116
+ # @overload ro_binds=
117
+ # Set given hash of paths to be bound with --ro-bind.
118
+ #
119
+ # Key is source path, value is destination path.
120
+ #
121
+ # Given source paths must exist.
76
122
  attr_reader :ro_binds
77
123
 
78
- # Path to temporary directory.
124
+ # @overload tmpdir
125
+ # Path to temporary directory.
79
126
  #
80
- # Defaults to Dir.tmpdir.
127
+ # Defaults to Dir.tmpdir.
128
+ # @overload tmpdir=(dir)
129
+ # Sets given directory as temporary directory for certain operations.
130
+ #
131
+ # @note Requires `dir` to be path to existing directory.
132
+ # @raise [RuntimeError] If given directory does not exist
133
+ # @param dir Path to temporary directory
81
134
  attr_reader :tmpdir
82
135
 
83
- # Methods to enable or disable feature sets to control various aspects of sandboxing.
84
- class Features
85
- # Defines Ruby feature set.
86
- class Ruby
87
- # @return [Array] list of needed libraries.
88
- attr_reader :stdlib
89
-
90
- def initialize
91
- @stdlib = []
92
- end
93
-
94
- # @see enabled=
95
- def enabled?
96
- @enabled
97
- end
98
-
99
- # Enable Ruby feature set.
100
- #
101
- # Among others, binds RbConfig::CONFIG["sitedir"] so scripts works.
102
- #
103
- # @note This does not allow development headers needed for compilation for now.
104
- # I’ll look at it after I have an use for it.
105
- def enable
106
- @enabled = true
107
- end
108
-
109
- # Disable Ruby feature set.
110
- def disable
111
- @enabled = false
112
- end
113
-
114
- # Extra libraries to be loaded from `RbConfig::CONFIG["rubyarchdir"]`.
115
- #
116
- # @note This is only required to be called if extra dependencies are necessary.
117
- # For example, psych.so requires libyaml.so.
118
- def stdlib= libs
119
- # Just a little check to have error earlier.
120
- libs.each do |lib|
121
- unless File.exist? "#{RbConfig::CONFIG["rubyarchdir"]}/#{lib}.so"
122
- raise "Library “#{lib}” passed to Bwrap::Config::Ruby.stdlib= does not exist."
123
- end
124
- end
125
-
126
- @stdlib = libs
127
- end
128
- end
129
-
130
- # @return [Ruby] Instance of feature class for Ruby
131
- def ruby
132
- @ruby ||= Ruby.new
133
- end
134
- end
135
-
136
136
  def initialize
137
+ @audio = []
137
138
  @binaries_from = []
139
+ @env_paths = []
140
+ @ro_binds = {}
138
141
  @tmpdir = Dir.tmpdir
139
- @audio = []
140
142
  end
141
143
 
142
144
  def binaries_from= array
@@ -178,14 +180,13 @@ class Bwrap::Config
178
180
  @root = directory
179
181
  end
180
182
 
181
- # Set given hash of paths to be bound with --ro-bind.
182
- #
183
- # Key is source path, value is destination path.
184
- #
185
- # Given source paths must exist.
186
183
  def ro_binds= binds
187
184
  @ro_binds = {}
188
185
  binds.each do |source_path, destination_path|
186
+ if destination_path.nil?
187
+ raise "binds should be key-value storage of Strings, for example a Hash."
188
+ end
189
+
189
190
  source_path = Pathname.new source_path
190
191
  unless source_path.exist?
191
192
  raise "Given read only bind does not exist. Please check path “#{source_path}” is correct."
@@ -197,14 +198,17 @@ class Bwrap::Config
197
198
  end
198
199
  end
199
200
 
200
- # Sets given directory as temporary directory for certain operations.
201
- #
202
- # @note Requires `dir` to be path to existing directory.
203
- # @raise [RuntimeError] If given directory does not exist
204
- # @param dir Path to temporary directory
201
+ # See attr_accessor :tmpdir for documentation.
205
202
  def tmpdir= dir
206
203
  raise "Directory to be set as a temporary directory, “#{dir}”, does not exist." unless Dir.exist? dir
207
204
 
208
205
  @tmpdir = dir
209
206
  end
207
+
208
+ # Add a path to sandbox instance’s PATH environment variable.
209
+ #
210
+ # @param path [String] Path to be added added to PATH environment variable
211
+ def add_env_path path
212
+ @env_paths << path
213
+ end
210
214
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bwrap::Execution
4
+ # Unspecified execution related error.
5
+ class CommandError < StandardError
6
+ end
7
+
8
+ # Signifies that command execution has failed.
9
+ class ExecutionFailed < CommandError
10
+ end
11
+
12
+ # Thrown if given command was not found.
13
+ class CommandNotFound < CommandError
14
+ # Command that was looked at.
15
+ attr_reader :command
16
+
17
+ def initialize command:
18
+ @command = command
19
+ msg = "Failed to find #{command} from PATH."
20
+
21
+ super msg
22
+ end
23
+ end
24
+ end
@@ -3,11 +3,14 @@
3
3
  # force_encoding modifies string, so can’t freeze strings.
4
4
 
5
5
  require "bwrap/output"
6
+ require_relative "exceptions"
6
7
  require_relative "execution"
7
8
 
8
9
  # Methods that performs actual execution logic.
9
10
  #
10
- # @api internal
11
+ # @note This is kind of pseudo-internal API. Hopefully there won’t be breaking changes.
12
+ # But please use {Bwrap::Execution} instead of relying functionality of this class,
13
+ # outside of {.prepend_rootcmd}.
11
14
  class Bwrap::Execution::Execute
12
15
  include Bwrap::Output
13
16