Class: Mixlib::ShellOut
- Inherits:
-
Object
- Object
- Mixlib::ShellOut
- Defined in:
- lib/mixlib/shellout.rb,
lib/mixlib/shellout/unix.rb,
lib/mixlib/shellout/helper.rb,
lib/mixlib/shellout/version.rb,
lib/mixlib/shellout/windows.rb,
lib/mixlib/shellout/exceptions.rb
Defined Under Namespace
Modules: Helper, Unix, Windows Classes: CommandTimeout, EmptyWindowsCommand, Error, InvalidCommandOption, ShellCommandFailed
Constant Summary collapse
- READ_WAIT_TIME =
0.01- READ_SIZE =
4096- DEFAULT_READ_TIMEOUT =
600- VERSION =
"3.4.10".freeze
Constants included from Windows
Constants included from Process::Constants
Process::Constants::ENVIRONMENT_BLOCK_ENDS, Process::Constants::ERROR_LOGON_TYPE_NOT_GRANTED, Process::Constants::ERROR_PRIVILEGE_NOT_HELD, Process::Constants::LOGON32_LOGON_BATCH, Process::Constants::LOGON32_LOGON_INTERACTIVE, Process::Constants::LOGON32_PROVIDER_DEFAULT, Process::Constants::UOI_NAME, Process::Constants::WAIT_ABANDONED, Process::Constants::WAIT_ABANDONED_0, Process::Constants::WAIT_FAILED, Process::Constants::WAIT_OBJECT_0, Process::Constants::WAIT_TIMEOUT, Process::Constants::WIN32_PROFILETYPE_LOCAL, Process::Constants::WIN32_PROFILETYPE_PT_MANDATORY, Process::Constants::WIN32_PROFILETYPE_PT_ROAMING, Process::Constants::WIN32_PROFILETYPE_PT_ROAMING_PREEXISTING, Process::Constants::WIN32_PROFILETYPE_PT_TEMPORARY
Instance Attribute Summary collapse
-
#cgroup ⇒ Object
Path to cgroupv2 that the process should run on.
-
#command ⇒ Object
readonly
The command to be executed.
-
#cwd ⇒ Object
Working directory for the subprocess.
-
#domain ⇒ Object
Returns the value of attribute domain.
-
#elevated ⇒ Object
Runs windows process with elevated privileges.
-
#environment ⇒ Object
Environment variables that will be set for the subcommand.
-
#execution_time ⇒ Object
readonly
The amount of time the subcommand took to execute.
-
#group ⇒ Object
Group the command will run as.
-
#input ⇒ Object
ShellOut will push data from :input down the stdin of the subprocess.
-
#live_stderr ⇒ Object
When live_stderr is set, the stderr of the subprocess will be copied to it as the subprocess is running.
-
#live_stdout ⇒ Object
When live_stdout is set, the stdout of the subprocess will be copied to it as the subprocess is running.
-
#log_level ⇒ Object
The log level at which ShellOut should log.
-
#log_tag ⇒ Object
A string which will be prepended to the log message.
-
#logger ⇒ Object
If a logger is set, ShellOut will log a message before it executes the command.
-
#login ⇒ Object
Whether to simulate logon as the user.
-
#password ⇒ Object
Returns the value of attribute password.
-
#process_status_pipe ⇒ Object
readonly
Returns the value of attribute process_status_pipe.
-
#sensitive ⇒ Object
Returns the value of attribute sensitive.
-
#status ⇒ Object
readonly
A Process::Status (or ducktype) object collected when the subprocess is reaped.
-
#stderr ⇒ Object
readonly
Data written to stderr by the subprocess.
-
#stderr_pipe ⇒ Object
readonly
Returns the value of attribute stderr_pipe.
-
#stdin_pipe ⇒ Object
readonly
Returns the value of attribute stdin_pipe.
-
#stdout ⇒ Object
readonly
Data written to stdout by the subprocess.
-
#stdout_pipe ⇒ Object
readonly
Returns the value of attribute stdout_pipe.
- #timeout ⇒ Object
-
#umask ⇒ Object
The umask that will be set for the subcommand.
-
#user ⇒ Object
User the command will run as.
-
#valid_exit_codes ⇒ Object
An Array of acceptable exit codes.
-
#with_logon ⇒ Object
TODO remove.
Instance Method Summary collapse
-
#error! ⇒ Object
If #error? is true, calls
invalid!, which raises an Exception. -
#error? ⇒ Boolean
Checks the
exitstatusagainst the set ofvalid_exit_codes. -
#exitstatus ⇒ Object
The exit status of the subprocess.
-
#format_for_exception ⇒ Object
Creates a String showing the output of the command, including a banner showing the exact command executed.
-
#gid ⇒ Object
The gid that the subprocess will switch to.
-
#initialize(*command_args) ⇒ ShellOut
constructor
Arguments: Takes a single command, or a list of command fragments.
- #inspect ⇒ Object
-
#invalid!(msg = nil) ⇒ Object
Raises a ShellCommandFailed exception, appending the command’s stdout, stderr, and exitstatus to the exception message.
-
#live_stream ⇒ Object
Returns the stream that both is being used by both live_stdout and live_stderr, or nil.
-
#live_stream=(stream) ⇒ Object
A shortcut for setting both live_stdout and live_stderr, so that both the stdout and stderr from the subprocess will be copied to the same stream as the subprocess is running.
-
#run_command ⇒ Object
Run the command, writing the command’s standard out and standard error to
stdoutandstderr, and saving its exit status object tostatus=== Returns returnsself;stdout,stderr,status, andexitstatuswill be populated with results of the command === Raises * Errno::EACCES when you are not privileged to execute the command * Errno::ENOENT when the command is not available on the system (or not in the current $PATH) * CommandTimeout when the command does not complete withintimeoutseconds (default: 600s). -
#uid ⇒ Object
The uid that the subprocess will switch to.
Methods included from Unix
#all_seconderies, #logon_environment, #process_environment, #sgids, #using_login?
Constructor Details
#initialize(*command_args) ⇒ ShellOut
Arguments:
Takes a single command, or a list of command fragments. These are used as arguments to Kernel.exec. See the Kernel.exec documentation for more explanation of how arguments are evaluated. The last argument can be an options Hash.
Options:
If the last argument is a Hash, it is removed from the list of args passed to exec and used as an options hash. The following options are available:
-
user: the user the command should run as. if an integer is given, it is used as a uid. A string is treated as a username and resolved to a uid with Etc.getpwnam -
group: the group the command should run as. works similarly touser -
cwd: the directory to chdir to before running the command -
umask: a umask to set before running the command. If given as an Integer, be sure to use two leading zeros so it’s parsed as Octal. A string will be treated as an octal integer -
returns: one or more Integer values to use as valid exit codes for the subprocess. This only has an effect if you callerror!afterrun_command. -
environment: a Hash of environment variables to set before the command is run. -
timeout: a Numeric value for the number of seconds to wait on the child process before raising an Exception. This is calculated as the total amount of time that ShellOut waited on the child process without receiving any output (i.e., IO.select returned nil). Default is 600 seconds. Note: the stdlib Timeout library is not used. -
input: A String of data to be passed to the subcommand. This is written to the child process’ stdin stream before the process is launched. The child’s stdin stream will be a pipe, so the size of input data should not exceed the system’s default pipe capacity (4096 bytes is a safe value, though on newer Linux systems the capacity is 64k by default). -
live_stream: An IO or Logger-like object (must respond to the append operator <<) that will receive data as ShellOut reads it from the child process. Generally this is used to copy data from the child to the parent’s stdout so that users may observe the progress of long-running commands. -
login: Whether to simulate a login (set secondary groups, primary group, environment variables etc) as done by the OS in an actual login
Examples:
Invoke find(1) to search for .rb files:
find = Mixlib::ShellOut.new("find . -name '*.rb'")
find.run_command
# If all went well, the results are on +stdout+
puts find.stdout
# find(1) prints diagnostic info to STDERR:
puts "error messages" + find.stderr
# Raise an exception if it didn't exit with 0
find.error!
Run a command as the www user with no extra ENV settings from /tmp
cmd = Mixlib::ShellOut.new("apachectl", "start", :user => 'www', :env => nil, :cwd => '/tmp')
cmd.run_command # etc.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/mixlib/shellout.rb', line 173 def initialize(*command_args) # Since ruby 4.0 will freeze string literals by default, we are assigning mutable strings here. @stdout, @stderr, @process_status = String.new(""), String.new(""), String.new("") @live_stdout = @live_stderr = nil @input = nil @log_level = :debug @log_tag = nil @environment = {} @cwd = nil @valid_exit_codes = [0] @terminate_reason = nil @timeout = nil @elevated = false @sensitive = false @cgroup = nil if command_args.last.is_a?(Hash) (command_args.pop) end @command = command_args.size == 1 ? command_args.first : command_args end |
Instance Attribute Details
#cgroup ⇒ Object
Path to cgroupv2 that the process should run on
119 120 121 |
# File 'lib/mixlib/shellout.rb', line 119 def cgroup @cgroup end |
#command ⇒ Object (readonly)
The command to be executed.
85 86 87 |
# File 'lib/mixlib/shellout.rb', line 85 def command @command end |
#cwd ⇒ Object
Working directory for the subprocess. Normally set via options to new
55 56 57 |
# File 'lib/mixlib/shellout.rb', line 55 def cwd @cwd end |
#domain ⇒ Object
Returns the value of attribute domain.
42 43 44 |
# File 'lib/mixlib/shellout.rb', line 42 def domain @domain end |
#elevated ⇒ Object
Runs windows process with elevated privileges. Required for Powershell commands which need elevated privileges
114 115 116 |
# File 'lib/mixlib/shellout.rb', line 114 def elevated @elevated end |
#environment ⇒ Object
Environment variables that will be set for the subcommand. Refer to the documentation of new to understand how ShellOut interprets this.
92 93 94 |
# File 'lib/mixlib/shellout.rb', line 92 def environment @environment end |
#execution_time ⇒ Object (readonly)
The amount of time the subcommand took to execute
99 100 101 |
# File 'lib/mixlib/shellout.rb', line 99 def execution_time @execution_time end |
#group ⇒ Object
Group the command will run as. Normally set via options passed to new
52 53 54 |
# File 'lib/mixlib/shellout.rb', line 52 def group @group end |
#input ⇒ Object
ShellOut will push data from :input down the stdin of the subprocess. Normally set via options passed to new. Default: nil
72 73 74 |
# File 'lib/mixlib/shellout.rb', line 72 def input @input end |
#live_stderr ⇒ Object
When live_stderr is set, the stderr of the subprocess will be copied to it as the subprocess is running.
67 68 69 |
# File 'lib/mixlib/shellout.rb', line 67 def live_stderr @live_stderr end |
#live_stdout ⇒ Object
When live_stdout is set, the stdout of the subprocess will be copied to it as the subprocess is running.
63 64 65 |
# File 'lib/mixlib/shellout.rb', line 63 def live_stdout @live_stdout end |
#log_level ⇒ Object
The log level at which ShellOut should log.
79 80 81 |
# File 'lib/mixlib/shellout.rb', line 79 def log_level @log_level end |
#log_tag ⇒ Object
A string which will be prepended to the log message.
82 83 84 |
# File 'lib/mixlib/shellout.rb', line 82 def log_tag @log_tag end |
#logger ⇒ Object
If a logger is set, ShellOut will log a message before it executes the command.
76 77 78 |
# File 'lib/mixlib/shellout.rb', line 76 def logger @logger end |
#login ⇒ Object
Whether to simulate logon as the user. Normally set via options passed to new Always enabled on windows
49 50 51 |
# File 'lib/mixlib/shellout.rb', line 49 def login @login end |
#password ⇒ Object
Returns the value of attribute password.
43 44 45 |
# File 'lib/mixlib/shellout.rb', line 43 def password @password end |
#process_status_pipe ⇒ Object (readonly)
Returns the value of attribute process_status_pipe.
111 112 113 |
# File 'lib/mixlib/shellout.rb', line 111 def process_status_pipe @process_status_pipe end |
#sensitive ⇒ Object
Returns the value of attribute sensitive.
116 117 118 |
# File 'lib/mixlib/shellout.rb', line 116 def sensitive @sensitive end |
#status ⇒ Object (readonly)
A Process::Status (or ducktype) object collected when the subprocess is reaped.
109 110 111 |
# File 'lib/mixlib/shellout.rb', line 109 def status @status end |
#stderr ⇒ Object (readonly)
Data written to stderr by the subprocess
105 106 107 |
# File 'lib/mixlib/shellout.rb', line 105 def stderr @stderr end |
#stderr_pipe ⇒ Object (readonly)
Returns the value of attribute stderr_pipe.
111 112 113 |
# File 'lib/mixlib/shellout.rb', line 111 def stderr_pipe @stderr_pipe end |
#stdin_pipe ⇒ Object (readonly)
Returns the value of attribute stdin_pipe.
111 112 113 |
# File 'lib/mixlib/shellout.rb', line 111 def stdin_pipe @stdin_pipe end |
#stdout ⇒ Object (readonly)
Data written to stdout by the subprocess
102 103 104 |
# File 'lib/mixlib/shellout.rb', line 102 def stdout @stdout end |
#stdout_pipe ⇒ Object (readonly)
Returns the value of attribute stdout_pipe.
111 112 113 |
# File 'lib/mixlib/shellout.rb', line 111 def stdout_pipe @stdout_pipe end |
#timeout ⇒ Object
233 234 235 |
# File 'lib/mixlib/shellout.rb', line 233 def timeout @timeout || DEFAULT_READ_TIMEOUT end |
#umask ⇒ Object
The umask that will be set for the subcommand.
88 89 90 |
# File 'lib/mixlib/shellout.rb', line 88 def umask @umask end |
#user ⇒ Object
User the command will run as. Normally set via options passed to new
41 42 43 |
# File 'lib/mixlib/shellout.rb', line 41 def user @user end |
#valid_exit_codes ⇒ Object
An Array of acceptable exit codes. #error? (and #error!) use this list to determine if the command was successful. Normally set via options to new
59 60 61 |
# File 'lib/mixlib/shellout.rb', line 59 def valid_exit_codes @valid_exit_codes end |
#with_logon ⇒ Object
TODO remove
45 46 47 |
# File 'lib/mixlib/shellout.rb', line 45 def with_logon @with_logon end |
Instance Method Details
#error! ⇒ Object
If #error? is true, calls invalid!, which raises an Exception.
Returns
- nil:
-
always returns nil when it does not raise
Raises
- ::ShellCommandFailed:
-
via
invalid!
294 295 296 |
# File 'lib/mixlib/shellout.rb', line 294 def error! invalid!("Expected process to exit with #{valid_exit_codes.inspect}, but received '#{exitstatus}'") if error? end |
#error? ⇒ Boolean
Checks the exitstatus against the set of valid_exit_codes.
Returns
true if exitstatus is not in the list of valid_exit_codes, false otherwise.
285 286 287 |
# File 'lib/mixlib/shellout.rb', line 285 def error? !Array(valid_exit_codes).include?(exitstatus) end |
#exitstatus ⇒ Object
The exit status of the subprocess. Will be nil if the command is still running or died without setting an exit status (e.g., terminated by ‘kill -9`).
256 257 258 |
# File 'lib/mixlib/shellout.rb', line 256 def exitstatus @status&.exitstatus end |
#format_for_exception ⇒ Object
Creates a String showing the output of the command, including a banner showing the exact command executed. Used by invalid! to show command results when the command exited with an unexpected status.
240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/mixlib/shellout.rb', line 240 def format_for_exception return "Command execution failed. STDOUT/STDERR suppressed for sensitive resource" if sensitive msg = String.new msg << "#{@terminate_reason}\n" if @terminate_reason msg << "---- Begin output of #{command} ----\n" msg << "STDOUT: #{stdout.strip}\n" msg << "STDERR: #{stderr.strip}\n" msg << "---- End output of #{command} ----\n" msg << "Ran #{command} returned #{status.exitstatus}" if status msg end |
#gid ⇒ Object
The gid that the subprocess will switch to. If the group attribute is given as a group name, it is converted to a gid by Etc.getgrnam TODO migrate to shellout/unix.rb
226 227 228 229 230 231 |
# File 'lib/mixlib/shellout.rb', line 226 def gid return group.is_a?(Integer) ? group : Etc.getgrnam(group.to_s).gid if group return Etc.getpwuid(uid).gid if using_login? nil end |
#inspect ⇒ Object
311 312 313 314 315 316 |
# File 'lib/mixlib/shellout.rb', line 311 def inspect "<#{self.class.name}##{object_id}: command: '#{@command}' process_status: #{@status.inspect} " + "stdout: '#{stdout.strip}' stderr: '#{stderr.strip}' child_pid: #{@child_pid.inspect} " + "environment: #{@environment.inspect} timeout: #{timeout} user: #{@user} group: #{@group} working_dir: #{@cwd} " + "cgroup: #{@cgroup} >" end |
#invalid!(msg = nil) ⇒ Object
Raises a ShellCommandFailed exception, appending the command’s stdout, stderr, and exitstatus to the exception message.
Arguments
msg: A String to use as the basis of the exception message. The default explanation is very generic, providing a more informative message is highly encouraged.
Raises
ShellCommandFailed always
306 307 308 309 |
# File 'lib/mixlib/shellout.rb', line 306 def invalid!(msg = nil) msg ||= "Command produced unexpected results" raise ShellCommandFailed, msg + "\n" + format_for_exception end |
#live_stream ⇒ Object
Returns the stream that both is being used by both live_stdout and live_stderr, or nil
197 198 199 |
# File 'lib/mixlib/shellout.rb', line 197 def live_stream live_stdout == live_stderr ? live_stdout : nil end |
#live_stream=(stream) ⇒ Object
A shortcut for setting both live_stdout and live_stderr, so that both the stdout and stderr from the subprocess will be copied to the same stream as the subprocess is running.
204 205 206 |
# File 'lib/mixlib/shellout.rb', line 204 def live_stream=(stream) @live_stdout = @live_stderr = stream end |
#run_command ⇒ Object
Run the command, writing the command’s standard out and standard error to stdout and stderr, and saving its exit status object to status
Returns
returns self; stdout, stderr, status, and exitstatus will be populated with results of the command
Raises
-
Errno::EACCES when you are not privileged to execute the command
-
Errno::ENOENT when the command is not available on the system (or not in the current $PATH)
-
CommandTimeout when the command does not complete within
timeoutseconds (default: 600s)
271 272 273 274 275 276 277 278 279 |
# File 'lib/mixlib/shellout.rb', line 271 def run_command if logger prefix = log_tag.nil? ? "" : "#{@log_tag} " = prefix + "sh(#{@command})" # log_message = (log_tag.nil? ? "" : "#{@log_tag} ") << "sh(#{@command})" logger.send(log_level, ) end super end |
#uid ⇒ Object
The uid that the subprocess will switch to. If the user attribute was given as a username, it is converted to a uid by Etc.getpwnam TODO migrate to shellout/unix.rb
217 218 219 220 221 |
# File 'lib/mixlib/shellout.rb', line 217 def uid return nil unless user user.is_a?(Integer) ? user : Etc.getpwnam(user.to_s).uid end |