ohai 0.5.8 → 0.6.0.beta.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +16 -48
- data/bin/ohai +1 -1
- data/lib/ohai.rb +1 -3
- data/lib/ohai/mash.rb +211 -0
- data/lib/ohai/mixin/command.rb +157 -44
- data/lib/ohai/mixin/ec2_metadata.rb +87 -0
- data/lib/ohai/plugins/c.rb +16 -13
- data/lib/ohai/plugins/chef.rb +2 -1
- data/lib/ohai/plugins/cloud.rb +25 -0
- data/lib/ohai/plugins/darwin/network.rb +10 -1
- data/lib/ohai/plugins/dmi.rb +100 -37
- data/lib/ohai/plugins/dmi_common.rb +117 -0
- data/lib/ohai/plugins/ec2.rb +12 -61
- data/lib/ohai/plugins/eucalyptus.rb +65 -0
- data/lib/ohai/plugins/freebsd/network.rb +11 -2
- data/lib/ohai/plugins/java.rb +4 -4
- data/lib/ohai/plugins/linux/filesystem.rb +24 -0
- data/lib/ohai/plugins/linux/network.rb +14 -1
- data/lib/ohai/plugins/linux/platform.rb +3 -0
- data/lib/ohai/plugins/linux/virtualization.rb +28 -6
- data/lib/ohai/plugins/netbsd/network.rb +10 -1
- data/lib/ohai/plugins/network.rb +3 -1
- data/lib/ohai/plugins/ohai.rb +1 -0
- data/lib/ohai/plugins/openbsd/network.rb +10 -1
- data/lib/ohai/plugins/ruby.rb +1 -1
- data/lib/ohai/plugins/sigar/filesystem.rb +2 -0
- data/lib/ohai/plugins/solaris2/dmi.rb +176 -0
- data/lib/ohai/plugins/solaris2/filesystem.rb +101 -0
- data/lib/ohai/plugins/solaris2/hostname.rb +10 -1
- data/lib/ohai/plugins/solaris2/network.rb +6 -1
- data/lib/ohai/plugins/solaris2/uptime.rb +36 -0
- data/lib/ohai/plugins/solaris2/virtualization.rb +91 -0
- data/lib/ohai/plugins/virtualization.rb +1 -1
- data/lib/ohai/plugins/windows/network.rb +17 -11
- data/lib/ohai/system.rb +22 -32
- data/lib/ohai/version.rb +23 -0
- data/spec/ohai/plugins/c_spec.rb +58 -7
- data/spec/ohai/plugins/cloud_spec.rb +24 -0
- data/spec/ohai/plugins/dmi_spec.rb +107 -47
- data/spec/ohai/plugins/ec2_spec.rb +3 -3
- data/spec/ohai/plugins/eucalyptus_spec.rb +84 -0
- data/spec/ohai/plugins/java_spec.rb +55 -2
- data/spec/ohai/plugins/linux/platform_spec.rb +13 -0
- data/spec/ohai/plugins/linux/virtualization_spec.rb +47 -6
- data/spec/ohai/plugins/perl_spec.rb +15 -14
- data/spec/ohai/plugins/rackspace_spec.rb +14 -14
- data/spec/ohai/plugins/solaris2/hostname_spec.rb +3 -8
- data/spec/ohai/plugins/solaris2/kernel_spec.rb +6 -6
- data/spec/ohai/plugins/solaris2/network_spec.rb +22 -22
- data/spec/ohai/plugins/solaris2/virtualization_spec.rb +133 -0
- data/spec/spec_helper.rb +5 -13
- metadata +182 -179
data/Rakefile
CHANGED
@@ -2,61 +2,29 @@ require 'rubygems'
|
|
2
2
|
require 'rake/gempackagetask'
|
3
3
|
require 'rubygems/specification'
|
4
4
|
require 'date'
|
5
|
-
require 'spec/rake/spectask'
|
6
5
|
|
7
|
-
|
8
|
-
GEM_VERSION = "0.5.8"
|
9
|
-
AUTHOR = "Adam Jacob"
|
10
|
-
EMAIL = "adam@opscode.com"
|
11
|
-
HOMEPAGE = "http://wiki.opscode.com/display/ohai"
|
12
|
-
SUMMARY = "Ohai profiles your system and emits JSON"
|
6
|
+
gemspec = eval(IO.read("ohai.gemspec"))
|
13
7
|
|
14
|
-
spec = Gem::Specification.new do |s|
|
15
|
-
s.name = GEM
|
16
|
-
s.version = GEM_VERSION
|
17
|
-
s.platform = Gem::Platform::RUBY
|
18
|
-
s.has_rdoc = true
|
19
|
-
s.summary = SUMMARY
|
20
|
-
s.description = s.summary
|
21
|
-
s.author = AUTHOR
|
22
|
-
s.email = EMAIL
|
23
|
-
s.homepage = HOMEPAGE
|
24
|
-
|
25
|
-
s.add_dependency "json", ">= 1.4.4", "<= 1.4.6"
|
26
|
-
s.add_dependency "extlib"
|
27
|
-
s.add_dependency "systemu"
|
28
|
-
s.add_dependency "mixlib-cli"
|
29
|
-
s.add_dependency "mixlib-config"
|
30
|
-
s.add_dependency "mixlib-log"
|
31
|
-
s.bindir = "bin"
|
32
|
-
s.executables = %w(ohai)
|
33
|
-
|
34
|
-
s.require_path = 'lib'
|
35
|
-
s.autorequire = GEM
|
36
|
-
s.files = %w(LICENSE README.rdoc Rakefile) + Dir.glob("{docs,lib,spec}/**/*")
|
37
|
-
end
|
38
|
-
|
39
|
-
task :default => :spec
|
40
|
-
|
41
|
-
desc "Run specs"
|
42
|
-
Spec::Rake::SpecTask.new do |t|
|
43
|
-
t.spec_files = FileList['spec/**/*_spec.rb']
|
44
|
-
t.spec_opts = %w(-fs --color)
|
45
|
-
end
|
46
8
|
|
47
|
-
|
48
|
-
Rake::GemPackageTask.new(spec) do |pkg|
|
49
|
-
pkg.gem_spec = spec
|
50
|
-
end
|
9
|
+
Rake::GemPackageTask.new(gemspec).define
|
51
10
|
|
52
11
|
desc "install the gem locally"
|
53
12
|
task :install => [:package] do
|
54
|
-
sh %{gem install pkg/#{
|
13
|
+
sh %{gem install pkg/#{ohai}-#{OHAI_VERSION}}
|
55
14
|
end
|
56
15
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
16
|
+
begin
|
17
|
+
require 'rspec/core/rake_task'
|
18
|
+
|
19
|
+
RSpec::Core::RakeTask.new do |t|
|
20
|
+
t.pattern = 'spec/**/*_spec.rb'
|
21
|
+
t.rspec_opts = %w(-fs --color)
|
22
|
+
end
|
23
|
+
rescue LoadError
|
24
|
+
desc "rspec is not installed, this task is disabled"
|
25
|
+
task :spec do
|
26
|
+
abort "rspec is not installed. `(sudo) gem install rspec` to run unit tests"
|
61
27
|
end
|
62
28
|
end
|
29
|
+
|
30
|
+
task :default => :spec
|
data/bin/ohai
CHANGED
@@ -29,7 +29,7 @@ end
|
|
29
29
|
begin
|
30
30
|
# if we're in a source code checkout, we want to run the code from that.
|
31
31
|
# have to do this *after* rubygems is loaded.
|
32
|
-
$:.unshift
|
32
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
33
33
|
require 'ohai/application'
|
34
34
|
rescue LoadError
|
35
35
|
if missing_rubygems
|
data/lib/ohai.rb
CHANGED
data/lib/ohai/mash.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
# Copyright (c) 2009 Dan Kubb
|
2
|
+
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
# ---
|
23
|
+
# ---
|
24
|
+
|
25
|
+
# Some portions of blank.rb and mash.rb are verbatim copies of software
|
26
|
+
# licensed under the MIT license. That license is included below:
|
27
|
+
|
28
|
+
# Copyright (c) 2005-2008 David Heinemeier Hansson
|
29
|
+
|
30
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
31
|
+
# a copy of this software and associated documentation files (the
|
32
|
+
# "Software"), to deal in the Software without restriction, including
|
33
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
34
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
35
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
36
|
+
# the following conditions:
|
37
|
+
|
38
|
+
# The above copyright notice and this permission notice shall be
|
39
|
+
# included in all copies or substantial portions of the Software.
|
40
|
+
|
41
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
42
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
43
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
44
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
45
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
46
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
47
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
48
|
+
|
49
|
+
# This class has dubious semantics and we only have it so that people can write
|
50
|
+
# params[:key] instead of params['key'].
|
51
|
+
class Mash < Hash
|
52
|
+
|
53
|
+
# @param constructor<Object>
|
54
|
+
# The default value for the mash. Defaults to an empty hash.
|
55
|
+
#
|
56
|
+
# @details [Alternatives]
|
57
|
+
# If constructor is a Hash, a new mash will be created based on the keys of
|
58
|
+
# the hash and no default value will be set.
|
59
|
+
def initialize(constructor = {})
|
60
|
+
if constructor.is_a?(Hash)
|
61
|
+
super()
|
62
|
+
update(constructor)
|
63
|
+
else
|
64
|
+
super(constructor)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @param key<Object> The default value for the mash. Defaults to nil.
|
69
|
+
#
|
70
|
+
# @details [Alternatives]
|
71
|
+
# If key is a Symbol and it is a key in the mash, then the default value will
|
72
|
+
# be set to the value matching the key.
|
73
|
+
def default(key = nil)
|
74
|
+
if key.is_a?(Symbol) && include?(key = key.to_s)
|
75
|
+
self[key]
|
76
|
+
else
|
77
|
+
super
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
82
|
+
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
83
|
+
|
84
|
+
# @param key<Object> The key to set.
|
85
|
+
# @param value<Object>
|
86
|
+
# The value to set the key to.
|
87
|
+
#
|
88
|
+
# @see Mash#convert_key
|
89
|
+
# @see Mash#convert_value
|
90
|
+
def []=(key, value)
|
91
|
+
regular_writer(convert_key(key), convert_value(value))
|
92
|
+
end
|
93
|
+
|
94
|
+
# @param other_hash<Hash>
|
95
|
+
# A hash to update values in the mash with. The keys and the values will be
|
96
|
+
# converted to Mash format.
|
97
|
+
#
|
98
|
+
# @return [Mash] The updated mash.
|
99
|
+
def update(other_hash)
|
100
|
+
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
|
101
|
+
self
|
102
|
+
end
|
103
|
+
|
104
|
+
alias_method :merge!, :update
|
105
|
+
|
106
|
+
# @param key<Object> The key to check for. This will be run through convert_key.
|
107
|
+
#
|
108
|
+
# @return [Boolean] True if the key exists in the mash.
|
109
|
+
def key?(key)
|
110
|
+
super(convert_key(key))
|
111
|
+
end
|
112
|
+
|
113
|
+
# def include? def has_key? def member?
|
114
|
+
alias_method :include?, :key?
|
115
|
+
alias_method :has_key?, :key?
|
116
|
+
alias_method :member?, :key?
|
117
|
+
|
118
|
+
# @param key<Object> The key to fetch. This will be run through convert_key.
|
119
|
+
# @param *extras<Array> Default value.
|
120
|
+
#
|
121
|
+
# @return [Object] The value at key or the default value.
|
122
|
+
def fetch(key, *extras)
|
123
|
+
super(convert_key(key), *extras)
|
124
|
+
end
|
125
|
+
|
126
|
+
# @param *indices<Array>
|
127
|
+
# The keys to retrieve values for. These will be run through +convert_key+.
|
128
|
+
#
|
129
|
+
# @return [Array] The values at each of the provided keys
|
130
|
+
def values_at(*indices)
|
131
|
+
indices.collect {|key| self[convert_key(key)]}
|
132
|
+
end
|
133
|
+
|
134
|
+
# @param hash<Hash> The hash to merge with the mash.
|
135
|
+
#
|
136
|
+
# @return [Mash] A new mash with the hash values merged in.
|
137
|
+
def merge(hash)
|
138
|
+
self.dup.update(hash)
|
139
|
+
end
|
140
|
+
|
141
|
+
# @param key<Object>
|
142
|
+
# The key to delete from the mash.\
|
143
|
+
def delete(key)
|
144
|
+
super(convert_key(key))
|
145
|
+
end
|
146
|
+
|
147
|
+
# @param *rejected<Array[(String, Symbol)] The mash keys to exclude.
|
148
|
+
#
|
149
|
+
# @return [Mash] A new mash without the selected keys.
|
150
|
+
#
|
151
|
+
# @example
|
152
|
+
# { :one => 1, :two => 2, :three => 3 }.except(:one)
|
153
|
+
# #=> { "two" => 2, "three" => 3 }
|
154
|
+
def except(*keys)
|
155
|
+
super(*keys.map {|k| convert_key(k)})
|
156
|
+
end
|
157
|
+
|
158
|
+
# Used to provide the same interface as Hash.
|
159
|
+
#
|
160
|
+
# @return [Mash] This mash unchanged.
|
161
|
+
def stringify_keys!; self end
|
162
|
+
|
163
|
+
# @return [Hash] The mash as a Hash with symbolized keys.
|
164
|
+
def symbolize_keys
|
165
|
+
h = Hash.new(default)
|
166
|
+
each { |key, val| h[key.to_sym] = val }
|
167
|
+
h
|
168
|
+
end
|
169
|
+
|
170
|
+
# @return [Hash] The mash as a Hash with string keys.
|
171
|
+
def to_hash
|
172
|
+
Hash.new(default).merge(self)
|
173
|
+
end
|
174
|
+
|
175
|
+
# @return [Mash] Convert a Hash into a Mash
|
176
|
+
# The input Hash's default value is maintained
|
177
|
+
def self.from_hash(hash)
|
178
|
+
mash = Mash.new(hash)
|
179
|
+
mash.default = hash.default
|
180
|
+
mash
|
181
|
+
end
|
182
|
+
|
183
|
+
protected
|
184
|
+
# @param key<Object> The key to convert.
|
185
|
+
#
|
186
|
+
# @param [Object]
|
187
|
+
# The converted key. If the key was a symbol, it will be converted to a
|
188
|
+
# string.
|
189
|
+
#
|
190
|
+
# @api private
|
191
|
+
def convert_key(key)
|
192
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
193
|
+
end
|
194
|
+
|
195
|
+
# @param value<Object> The value to convert.
|
196
|
+
#
|
197
|
+
# @return [Object]
|
198
|
+
# The converted value. A Hash or an Array of hashes, will be converted to
|
199
|
+
# their Mash equivalents.
|
200
|
+
#
|
201
|
+
# @api private
|
202
|
+
def convert_value(value)
|
203
|
+
if value.class == Hash
|
204
|
+
Mash.from_hash(value)
|
205
|
+
elsif value.is_a?(Array)
|
206
|
+
value.collect { |e| convert_value(e) }
|
207
|
+
else
|
208
|
+
value
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
data/lib/ohai/mixin/command.rb
CHANGED
@@ -6,9 +6,9 @@
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
8
8
|
# You may obtain a copy of the License at
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
11
|
+
#
|
12
12
|
# Unless required by applicable law or agreed to in writing, software
|
13
13
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
14
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
@@ -27,42 +27,30 @@ require 'systemu'
|
|
27
27
|
module Ohai
|
28
28
|
module Mixin
|
29
29
|
module Command
|
30
|
-
|
31
|
-
def run_command(args={})
|
30
|
+
|
31
|
+
def run_command(args={})
|
32
32
|
if args.has_key?(:creates)
|
33
33
|
if File.exists?(args[:creates])
|
34
34
|
Ohai::Log.debug("Skipping #{args[:command]} - creates #{args[:creates]} exists.")
|
35
35
|
return false
|
36
36
|
end
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
stdout_string = nil
|
40
40
|
stderr_string = nil
|
41
|
-
|
42
|
-
args[:cwd] ||= Dir.tmpdir
|
41
|
+
|
42
|
+
args[:cwd] ||= Dir.tmpdir
|
43
43
|
unless File.directory?(args[:cwd])
|
44
44
|
raise Ohai::Exceptions::Exec, "#{args[:cwd]} does not exist or is not a directory"
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
status = nil
|
48
48
|
Dir.chdir(args[:cwd]) do
|
49
|
-
|
50
|
-
begin
|
51
|
-
Timeout.timeout(args[:timeout]) do
|
52
|
-
status, stdout_string, stderr_string = systemu(args[:command])
|
53
|
-
end
|
54
|
-
rescue Exception => e
|
55
|
-
Ohai::Log.error("#{args[:command_string]} exceeded timeout #{args[:timeout]}")
|
56
|
-
raise(e)
|
57
|
-
end
|
58
|
-
else
|
59
|
-
status, stdout_string, stderr_string = systemu(args[:command])
|
60
|
-
end
|
61
|
-
|
49
|
+
status, stdout_string, stderr_string = run_command_backend(args[:command], args[:timeout])
|
62
50
|
# systemu returns 42 when it hits unexpected errors
|
63
51
|
if status.exitstatus == 42 and stderr_string == ""
|
64
52
|
stderr_string = "Failed to run: #{args[:command]}, assuming command not found"
|
65
|
-
Ohai::Log.debug(stderr_string)
|
53
|
+
Ohai::Log.debug(stderr_string)
|
66
54
|
end
|
67
55
|
|
68
56
|
if stdout_string
|
@@ -75,7 +63,7 @@ module Ohai
|
|
75
63
|
Ohai::Log.debug(stderr_string.strip)
|
76
64
|
Ohai::Log.debug("---- End #{args[:command]} STDERR ----")
|
77
65
|
end
|
78
|
-
|
66
|
+
|
79
67
|
args[:returns] ||= 0
|
80
68
|
args[:no_status_check] ||= false
|
81
69
|
if status.exitstatus != args[:returns] and not args[:no_status_check]
|
@@ -88,16 +76,66 @@ module Ohai
|
|
88
76
|
end
|
89
77
|
|
90
78
|
module_function :run_command
|
91
|
-
|
92
|
-
|
79
|
+
|
80
|
+
def run_command_unix(command, timeout)
|
81
|
+
stderr_string, stdout_string, status = "", "", nil
|
82
|
+
|
83
|
+
exec_processing_block = lambda do |pid, stdin, stdout, stderr|
|
84
|
+
stdout_string, stderr_string = stdout.string.chomp, stderr.string.chomp
|
85
|
+
end
|
86
|
+
|
87
|
+
if timeout
|
88
|
+
begin
|
89
|
+
Timeout.timeout(timeout) do
|
90
|
+
status = popen4(command, {}, &exec_processing_block)
|
91
|
+
end
|
92
|
+
rescue Timeout::Error => e
|
93
|
+
Chef::Log.error("#{command} exceeded timeout #{timeout}")
|
94
|
+
raise(e)
|
95
|
+
end
|
96
|
+
else
|
97
|
+
status = popen4(command, {}, &exec_processing_block)
|
98
|
+
end
|
99
|
+
return status, stdout_string, stderr_string
|
100
|
+
end
|
101
|
+
|
102
|
+
def run_comand_windows(command, timeout)
|
103
|
+
if timeout
|
104
|
+
begin
|
105
|
+
systemu(command)
|
106
|
+
rescue SystemExit => e
|
107
|
+
raise
|
108
|
+
rescue Timeout::Error => e
|
109
|
+
Ohai::Log.error("#{command} exceeded timeout #{timeout}")
|
110
|
+
raise(e)
|
111
|
+
end
|
112
|
+
else
|
113
|
+
systemu(command)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
|
118
|
+
alias :run_command_backend :run_command_windows
|
119
|
+
else
|
120
|
+
alias :run_command_backend :run_command_unix
|
121
|
+
end
|
122
|
+
# This is taken directly from Ara T Howard's Open4 library, and then
|
93
123
|
# modified to suit the needs of Ohai. Any bugs here are most likely
|
94
124
|
# my own, and not Ara's.
|
95
125
|
#
|
96
|
-
# The original appears in external/open4.rb in its unmodified form.
|
126
|
+
# The original appears in external/open4.rb in its unmodified form.
|
97
127
|
#
|
98
128
|
# Thanks Ara!
|
99
129
|
def popen4(cmd, args={}, &b)
|
100
|
-
|
130
|
+
|
131
|
+
# Waitlast - this is magic.
|
132
|
+
#
|
133
|
+
# Do we wait for the child process to die before we yield
|
134
|
+
# to the block, or after? That is the magic of waitlast.
|
135
|
+
#
|
136
|
+
# By default, we are waiting before we yield the block.
|
137
|
+
args[:waitlast] ||= false
|
138
|
+
|
101
139
|
args[:user] ||= nil
|
102
140
|
unless args[:user].kind_of?(Integer)
|
103
141
|
args[:user] = Etc.getpwnam(args[:user]).uid if args[:user]
|
@@ -114,7 +152,7 @@ module Ohai
|
|
114
152
|
unless args[:environment].has_key?("LC_ALL")
|
115
153
|
args[:environment]["LC_ALL"] = "C"
|
116
154
|
end
|
117
|
-
|
155
|
+
|
118
156
|
pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
|
119
157
|
|
120
158
|
verbose = $VERBOSE
|
@@ -123,6 +161,8 @@ module Ohai
|
|
123
161
|
ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
124
162
|
|
125
163
|
cid = fork {
|
164
|
+
Process.setsid
|
165
|
+
|
126
166
|
pw.last.close
|
127
167
|
STDIN.reopen pw.first
|
128
168
|
pw.first.close
|
@@ -137,27 +177,32 @@ module Ohai
|
|
137
177
|
|
138
178
|
STDOUT.sync = STDERR.sync = true
|
139
179
|
|
140
|
-
if args[:user]
|
141
|
-
Process.euid = args[:user]
|
142
|
-
Process.uid = args[:user]
|
143
|
-
end
|
144
|
-
|
145
180
|
if args[:group]
|
146
181
|
Process.egid = args[:group]
|
147
182
|
Process.gid = args[:group]
|
148
183
|
end
|
149
|
-
|
184
|
+
|
185
|
+
if args[:user]
|
186
|
+
Process.euid = args[:user]
|
187
|
+
Process.uid = args[:user]
|
188
|
+
end
|
189
|
+
|
150
190
|
args[:environment].each do |key,value|
|
151
191
|
ENV[key] = value
|
152
192
|
end
|
153
|
-
|
193
|
+
|
194
|
+
if args[:umask]
|
195
|
+
umask = ((args[:umask].respond_to?(:oct) ? args[:umask].oct : args[:umask].to_i) & 007777)
|
196
|
+
File.umask(umask)
|
197
|
+
end
|
198
|
+
|
154
199
|
begin
|
155
200
|
if cmd.kind_of?(Array)
|
156
201
|
exec(*cmd)
|
157
202
|
else
|
158
203
|
exec(cmd)
|
159
204
|
end
|
160
|
-
raise 'forty-two'
|
205
|
+
raise 'forty-two'
|
161
206
|
rescue Exception => e
|
162
207
|
Marshal.dump(e, ps.last)
|
163
208
|
ps.last.flush
|
@@ -173,9 +218,6 @@ module Ohai
|
|
173
218
|
|
174
219
|
begin
|
175
220
|
e = Marshal.load ps.first
|
176
|
-
# If we get here, exec failed. Collect status of child to prevent
|
177
|
-
# zombies.
|
178
|
-
Process.waitpid(cid)
|
179
221
|
raise(Exception === e ? e : "unknown failure!")
|
180
222
|
rescue EOFError # If we get an EOF error, then the exec was successful
|
181
223
|
42
|
@@ -187,18 +229,89 @@ module Ohai
|
|
187
229
|
|
188
230
|
pi = [pw.last, pr.first, pe.first]
|
189
231
|
|
190
|
-
if b
|
232
|
+
if b
|
191
233
|
begin
|
192
|
-
|
193
|
-
|
234
|
+
if args[:waitlast]
|
235
|
+
b[cid, *pi]
|
236
|
+
# send EOF so that if the child process is reading from STDIN
|
237
|
+
# it will actually finish up and exit
|
238
|
+
pi[0].close_write
|
239
|
+
Process.waitpid2(cid).last
|
240
|
+
else
|
241
|
+
# This took some doing.
|
242
|
+
# The trick here is to close STDIN
|
243
|
+
# Then set our end of the childs pipes to be O_NONBLOCK
|
244
|
+
# Then wait for the child to die, which means any IO it
|
245
|
+
# wants to do must be done - it's dead. If it isn't,
|
246
|
+
# it's because something totally skanky is happening,
|
247
|
+
# and we don't care.
|
248
|
+
o = StringIO.new
|
249
|
+
e = StringIO.new
|
250
|
+
|
251
|
+
#pi[0].close
|
252
|
+
|
253
|
+
stdout = pi[1]
|
254
|
+
stderr = pi[2]
|
255
|
+
|
256
|
+
stdout.sync = true
|
257
|
+
stderr.sync = true
|
258
|
+
|
259
|
+
stdout.fcntl(Fcntl::F_SETFL, pi[1].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
|
260
|
+
stderr.fcntl(Fcntl::F_SETFL, pi[2].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
|
261
|
+
|
262
|
+
stdout_finished = false
|
263
|
+
stderr_finished = false
|
264
|
+
|
265
|
+
results = nil
|
266
|
+
|
267
|
+
while !stdout_finished || !stderr_finished
|
268
|
+
begin
|
269
|
+
channels_to_watch = []
|
270
|
+
channels_to_watch << stdout if !stdout_finished
|
271
|
+
channels_to_watch << stderr if !stderr_finished
|
272
|
+
ready = IO.select(channels_to_watch, nil, nil, 1.0)
|
273
|
+
rescue Errno::EAGAIN
|
274
|
+
ensure
|
275
|
+
results = Process.waitpid2(cid, Process::WNOHANG)
|
276
|
+
if results
|
277
|
+
stdout_finished = true
|
278
|
+
stderr_finished = true
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
if ready && ready.first.include?(stdout)
|
283
|
+
line = results ? stdout.gets(nil) : stdout.gets
|
284
|
+
if line
|
285
|
+
o.write(line)
|
286
|
+
else
|
287
|
+
stdout_finished = true
|
288
|
+
end
|
289
|
+
end
|
290
|
+
if ready && ready.first.include?(stderr)
|
291
|
+
line = results ? stderr.gets(nil) : stderr.gets
|
292
|
+
if line
|
293
|
+
e.write(line)
|
294
|
+
else
|
295
|
+
stderr_finished = true
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
results = Process.waitpid2(cid) unless results
|
300
|
+
o.rewind
|
301
|
+
e.rewind
|
302
|
+
b[cid, pi[0], o, e]
|
303
|
+
results.last
|
304
|
+
end
|
194
305
|
ensure
|
195
306
|
pi.each{|fd| fd.close unless fd.closed?}
|
196
307
|
end
|
197
308
|
else
|
198
309
|
[cid, pw.last, pr.first, pe.first]
|
199
310
|
end
|
200
|
-
|
201
|
-
|
311
|
+
rescue Errno::ENOENT
|
312
|
+
raise Ohai::Exceptions::Exec, "command #{cmd} doesn't exist or is not in the PATH"
|
313
|
+
end
|
314
|
+
|
202
315
|
module_function :popen4
|
203
316
|
end
|
204
317
|
end
|