Module: Mixlib::ShellOut::Unix

Included in:
Mixlib::ShellOut
Defined in:
lib/mixlib/shellout/unix.rb

Constant Summary collapse

ONE_DOT_EIGHT_DOT_SEVEN =

“1.8.7” as a frozen string. We use this with a hack that disables GC to avoid segfaults on Ruby 1.8.7, so we need to allocate the fewest objects we possibly can.

"1.8.7".freeze

Instance Method Summary collapse

Instance Method Details

#all_seconderiesObject

Helper method for sgids



41
42
43
44
45
46
47
48
49
# File 'lib/mixlib/shellout/unix.rb', line 41

def all_seconderies
  ret = []
  Etc.endgrent
  while ( g = Etc.getgrent )
    ret << g
  end
  Etc.endgrent
  ret
end

#logon_environmentObject

The environment variables that are deduced from simulating logon Only valid if login is used



63
64
65
66
67
68
69
70
71
# File 'lib/mixlib/shellout/unix.rb', line 63

def logon_environment
  return {} unless using_login?

  entry = Etc.getpwuid(uid)
  # According to `man su`, the set fields are:
  #  $HOME, $SHELL, $USER, $LOGNAME, $PATH, and $IFS
  # Values are copied from "shadow" package in Ubuntu 14.10
  { "HOME" => entry.dir, "SHELL" => entry.shell, "USER" => entry.name, "LOGNAME" => entry.name, "PATH" => "/sbin:/bin:/usr/sbin:/usr/bin", "IFS" => "\t\n" }
end

#process_environmentObject

Merges the two environments for the process



74
75
76
# File 'lib/mixlib/shellout/unix.rb', line 74

def process_environment
  logon_environment.merge(environment)
end

#run_commandObject

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)

  • Chef::Exceptions::CommandTimeout when the command does not complete within timeout seconds (default: 600s). When this happens, ShellOut will send a TERM and then KILL to the entire process group to ensure that any grandchild processes are terminated. If the invocation of the child process spawned multiple child processes (which commonly happens if the command is passed as a single string to be interpreted by bin/sh, and bin/sh is not bash), the exit status object may not contain the correct exit code of the process (of course there is no exit code if the command is killed by SIGKILL, also).



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/mixlib/shellout/unix.rb', line 96

def run_command
  @child_pid = fork_subprocess
  @reaped = false

  configure_parent_process_file_descriptors

  # Ruby 1.8.7 and 1.8.6 from mid 2009 try to allocate objects during GC
  # when calling IO.select and IO#read. Disabling GC works around the
  # segfault, but obviously it's a bad workaround. We no longer support
  # 1.8.6 so we only need this hack for 1.8.7.
  GC.disable if RUBY_VERSION == ONE_DOT_EIGHT_DOT_SEVEN

  # CHEF-3390: Marshall.load on Ruby < 1.8.7p369 also has a GC bug related
  # to Marshall.load, so try disabling GC first.
  propagate_pre_exec_failure

  @status = nil
  @result = nil
  @execution_time = 0

  write_to_child_stdin

  until @status
    ready_buffers = attempt_buffer_read
    unless ready_buffers
      @execution_time += READ_WAIT_TIME
      if @execution_time >= timeout && !@result
        # kill the bad proccess
        reap_errant_child
        # read anything it wrote when we killed it
        attempt_buffer_read
        # raise
        raise CommandTimeout, "Command timed out after #{@execution_time.to_i}s:\n#{format_for_exception}"
      end
    end

    attempt_reap
  end

  self
rescue Errno::ENOENT
  # When ENOENT happens, we can be reasonably sure that the child process
  # is going to exit quickly, so we use the blocking variant of waitpid2
  reap
  raise
ensure
  reap_errant_child if should_reap?
  # make one more pass to get the last of the output after the
  # child process dies
  attempt_buffer_read
  # no matter what happens, turn the GC back on, and hope whatever busted
  # version of ruby we're on doesn't allocate some objects during the next
  # GC run.
  GC.enable
  close_all_pipes
end

#sgidsObject

The secondary groups that the subprocess will switch to. Currently valid only if login is used, and is set to the user’s secondary groups



54
55
56
57
58
59
# File 'lib/mixlib/shellout/unix.rb', line 54

def sgids
  return nil unless using_login?

  user_name = Etc.getpwuid(uid).name
  all_seconderies.select { |g| g.mem.include?(user_name) }.map(&:gid)
end

#using_login?Boolean

Whether we’re simulating a login shell

Returns:

  • (Boolean)


36
37
38
# File 'lib/mixlib/shellout/unix.rb', line 36

def using_login?
   && user
end

#validate_options(opts) ⇒ Object

Option validation that is unix specific



29
30
31
32
33
# File 'lib/mixlib/shellout/unix.rb', line 29

def validate_options(opts)
  if opts[:elevated]
    raise InvalidCommandOption, "Option `elevated` is supported for Powershell commands only"
  end
end