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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +7 -0
- data/lib/bwrap/args/args.rb +33 -4
- data/lib/bwrap/args/bind/device.rb +48 -0
- data/lib/bwrap/args/bind/library.rb +63 -47
- data/lib/bwrap/args/bind.rb +43 -41
- data/lib/bwrap/args/construct.rb +22 -35
- data/lib/bwrap/args/environment.rb +5 -1
- data/lib/bwrap/args/features/ruby_binds.rb +3 -1
- data/lib/bwrap/args/features.rb +3 -4
- data/lib/bwrap/args/mount.rb +1 -7
- data/lib/bwrap/args/network.rb +2 -2
- data/lib/bwrap/args/user.rb +36 -0
- data/lib/bwrap/bwrap.rb +57 -27
- data/lib/bwrap/config.rb +34 -7
- data/lib/bwrap/execution/exec.rb +78 -0
- data/lib/bwrap/execution/execute.rb +25 -43
- data/lib/bwrap/execution/execution.rb +77 -50
- data/lib/bwrap/execution/logging.rb +49 -0
- data/lib/bwrap/execution/popen2e.rb +84 -12
- data/lib/bwrap/resolvers/executable.rb +70 -0
- data/lib/bwrap/{args → resolvers}/library.rb +38 -13
- data/lib/bwrap/resolvers/mime.rb +75 -0
- data/lib/bwrap/resolvers/resolvers.rb +7 -0
- data/lib/bwrap/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +10 -4
- metadata.gz.sig +0 -0
- data/lib/bwrap/args/bind/mime.rb +0 -65
data/lib/bwrap/args/mount.rb
CHANGED
@@ -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
|
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"
|
data/lib/bwrap/args/network.rb
CHANGED
@@ -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
|
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
|
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
|
-
|
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
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
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 =
|
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
|
-
#
|
93
|
-
#
|
94
|
-
#
|
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
|
-
#
|
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
|
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
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
51
|
-
return command if rootcmd.nil?
|
36
|
+
prepare_for_execution command
|
52
37
|
|
53
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
# @
|
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
|
-
|
44
|
-
|
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
|
-
|
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
|
-
|
59
|
-
|
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
|
-
|
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
|
-
|
99
|
+
@last_status = executor.last_status if executor&.last_status
|
79
100
|
end
|
80
101
|
|
81
|
-
# Works similarly to
|
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
|
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
|
-
#
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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 =
|
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]
|
145
|
+
kwargs[:log_callback] ||= 1
|
122
146
|
else
|
123
|
-
kwargs = { log_callback:
|
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
|