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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +16 -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 +30 -41
- data/lib/bwrap/execution/execution.rb +86 -35
- data/lib/bwrap/execution/logging.rb +49 -0
- data/lib/bwrap/execution/popen2e.rb +159 -0
- 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 +13 -6
- 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,28 +22,27 @@ class Bwrap::Execution::Execute
|
|
22
22
|
attr_accessor :dry_run
|
23
23
|
end
|
24
24
|
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
36
|
+
prepare_for_execution command
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
# @
|
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
|
-
|
43
|
-
|
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
|
-
|
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
|
-
|
58
|
-
|
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
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
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]
|
145
|
+
kwargs[:log_callback] ||= 1
|
98
146
|
else
|
99
|
-
kwargs = { log_callback:
|
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
|