mirror of
https://github.com/inspec/inspec
synced 2024-11-23 05:03:07 +00:00
Support finding larger processes on Busybox (#3446)
For larger processes, Busybox's ps displays the vsz and rss columns in megabytes or gigabytes, with no option I've found to override the behavior. This change updates the process regex to account for that and converts the values to kilobytes so they can still be cast as integers. Signed-off-by: Jonathan Hartman <j@hartman.io>
This commit is contained in:
parent
3248af1fe3
commit
7451917223
5 changed files with 93 additions and 4 deletions
|
@ -126,7 +126,7 @@ module Inspec::Resources
|
||||||
def ps_configuration_for_linux
|
def ps_configuration_for_linux
|
||||||
if busybox_ps?
|
if busybox_ps?
|
||||||
command = 'ps -o pid,vsz,rss,tty,stat,time,ruser,args'
|
command = 'ps -o pid,vsz,rss,tty,stat,time,ruser,args'
|
||||||
regex = /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/
|
regex = /^\s*(\d+)\s+(\d+(?:\.\d+)?[gm]?)\s+(\d+(?:\.\d+)?[gm]?)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/
|
||||||
field_map = {
|
field_map = {
|
||||||
pid: 1,
|
pid: 1,
|
||||||
vsz: 2,
|
vsz: 2,
|
||||||
|
@ -163,6 +163,18 @@ module Inspec::Resources
|
||||||
@busybox_ps ||= inspec.command('ps --help').stderr.include?('BusyBox')
|
@busybox_ps ||= inspec.command('ps --help').stderr.include?('BusyBox')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def convert_to_kilobytes(param)
|
||||||
|
return param.to_i unless param.is_a?(String)
|
||||||
|
|
||||||
|
if param.end_with?('g')
|
||||||
|
(param[0..-2].to_f * 1024 * 1024).to_i
|
||||||
|
elsif param.end_with?('m')
|
||||||
|
(param[0..-2].to_f * 1024).to_i
|
||||||
|
else
|
||||||
|
param.to_i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def build_process_list(command, regex, field_map)
|
def build_process_list(command, regex, field_map)
|
||||||
cmd = inspec.command(command)
|
cmd = inspec.command(command)
|
||||||
all = cmd.stdout.split("\n")[1..-1]
|
all = cmd.stdout.split("\n")[1..-1]
|
||||||
|
@ -187,8 +199,12 @@ module Inspec::Resources
|
||||||
end
|
end
|
||||||
|
|
||||||
# ensure pid, vsz, and rss are integers for backward compatibility
|
# ensure pid, vsz, and rss are integers for backward compatibility
|
||||||
[:pid, :vsz, :rss].each do |int_param|
|
process_data[:pid] = process_data[:pid].to_i if process_data.key?(:pid)
|
||||||
process_data[int_param] = process_data[int_param].to_i if process_data.key?(int_param)
|
|
||||||
|
# some ps variants (*cough* busybox) display vsz and rss as human readable MB or GB
|
||||||
|
[:vsz, :rss].each do |param|
|
||||||
|
next unless process_data.key?(param)
|
||||||
|
process_data[param] = convert_to_kilobytes(process_data[param])
|
||||||
end
|
end
|
||||||
|
|
||||||
# strip any newlines off the command
|
# strip any newlines off the command
|
||||||
|
|
|
@ -206,6 +206,11 @@ class MockLoader
|
||||||
mock.mock_command('', stdout, '', 0)
|
mock.mock_command('', stdout, '', 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd_stderr = lambda { |x = nil|
|
||||||
|
stderr = x.nil? ? '' : File.read(File.join(scriptpath, 'unit/mock/cmd', x))
|
||||||
|
mock.mock_command('', '', stderr, 1)
|
||||||
|
}
|
||||||
|
|
||||||
empty = lambda {
|
empty = lambda {
|
||||||
mock.mock_command('', '', '', 0)
|
mock.mock_command('', '', '', 0)
|
||||||
}
|
}
|
||||||
|
@ -235,7 +240,6 @@ class MockLoader
|
||||||
'ps axo pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user,command' => cmd.call('ps-axo'),
|
'ps axo pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user,command' => cmd.call('ps-axo'),
|
||||||
'ps axo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command' => cmd.call('ps-axoZ'),
|
'ps axo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command' => cmd.call('ps-axoZ'),
|
||||||
'ps -o pid,vsz,rss,tty,stat,time,ruser,args' => cmd.call('ps-busybox'),
|
'ps -o pid,vsz,rss,tty,stat,time,ruser,args' => cmd.call('ps-busybox'),
|
||||||
'ps --help' => empty.call,
|
|
||||||
'env' => cmd.call('env'),
|
'env' => cmd.call('env'),
|
||||||
'${Env:PATH}' => cmd.call('$env-PATH'),
|
'${Env:PATH}' => cmd.call('$env-PATH'),
|
||||||
# registry key test using winrm 2.0
|
# registry key test using winrm 2.0
|
||||||
|
@ -539,6 +543,7 @@ class MockLoader
|
||||||
# allow the ss and/or netstat commands to exist so the later mock is called
|
# allow the ss and/or netstat commands to exist so the later mock is called
|
||||||
if @platform && @platform[:name] == 'alpine'
|
if @platform && @platform[:name] == 'alpine'
|
||||||
mock_cmds.merge!(
|
mock_cmds.merge!(
|
||||||
|
'ps --help' => cmd_stderr.call('ps-help-busybox'),
|
||||||
%{bash -c 'type "netstat"'} => cmd_exit_1.call(),
|
%{bash -c 'type "netstat"'} => cmd_exit_1.call(),
|
||||||
%{bash -c 'type "ss"'} => cmd_exit_1.call(),
|
%{bash -c 'type "ss"'} => cmd_exit_1.call(),
|
||||||
%{which "ss"} => cmd_exit_1.call(),
|
%{which "ss"} => cmd_exit_1.call(),
|
||||||
|
@ -547,6 +552,7 @@ class MockLoader
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
mock_cmds.merge!(
|
mock_cmds.merge!(
|
||||||
|
'ps --help' => empty.call(),
|
||||||
%{bash -c 'type "ss"'} => empty.call(),
|
%{bash -c 'type "ss"'} => empty.call(),
|
||||||
%{bash -c 'type "netstat"'} => empty.call(),
|
%{bash -c 'type "netstat"'} => empty.call(),
|
||||||
'ss -tulpen' => cmd.call('ss-tulpen'),
|
'ss -tulpen' => cmd.call('ss-tulpen'),
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
PID VSZ RSS TT STAT TIME RUSER COMMAND
|
PID VSZ RSS TT STAT TIME RUSER COMMAND
|
||||||
1 1536 4 136,0 S 0:00 root /bin/sh
|
1 1536 4 136,0 S 0:00 root /bin/sh
|
||||||
5 1528 4 136,0 R 0:00 joe /some/other/coolprogram
|
5 1528 4 136,0 R 0:00 joe /some/other/coolprogram
|
||||||
|
82 24m 2m ? S 3:50 frank /a/bigger/program
|
||||||
|
83 2.6g 1g ? S 39:00 tim /the/biggest/program
|
||||||
|
|
8
test/unit/mock/cmd/ps-help-busybox
Normal file
8
test/unit/mock/cmd/ps-help-busybox
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
BusyBox v1.28.1 (2018-06-08 10:27:33 UTC) multi-call binary.
|
||||||
|
|
||||||
|
Usage: ps [-o COL1,COL2=HEADER] [-T]
|
||||||
|
|
||||||
|
Show list of processes
|
||||||
|
|
||||||
|
-o COL1,COL2=HEADER Select columns for display
|
||||||
|
-T Show threads
|
|
@ -141,6 +141,63 @@ describe 'Inspec::Resources::Processes' do
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'handles regular processes from busybox' do
|
||||||
|
resource = MockLoader.new(:alpine).load_resource('processes', '/some/other/coolprogram')
|
||||||
|
_(resource.entries.length).must_equal 1
|
||||||
|
_(resource.entries[0].to_h).must_equal({
|
||||||
|
label: nil,
|
||||||
|
pid: 5,
|
||||||
|
cpu: nil,
|
||||||
|
mem: nil,
|
||||||
|
vsz: 1528,
|
||||||
|
rss: 4,
|
||||||
|
tty: '136,0',
|
||||||
|
stat: 'R',
|
||||||
|
start: nil,
|
||||||
|
time: '0:00',
|
||||||
|
user: 'joe',
|
||||||
|
command: '/some/other/coolprogram',
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'handles human readable megabytes from busybox' do
|
||||||
|
resource = MockLoader.new(:alpine).load_resource('processes', '/a/bigger/program')
|
||||||
|
_(resource.entries.length).must_equal 1
|
||||||
|
_(resource.entries[0].to_h).must_equal({
|
||||||
|
label: nil,
|
||||||
|
pid: 82,
|
||||||
|
cpu: nil,
|
||||||
|
mem: nil,
|
||||||
|
vsz: 24576,
|
||||||
|
rss: 2048,
|
||||||
|
tty: '?',
|
||||||
|
stat: 'S',
|
||||||
|
start: nil,
|
||||||
|
time: '3:50',
|
||||||
|
user: 'frank',
|
||||||
|
command: '/a/bigger/program',
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'handles human readable gigabytes from busybox' do
|
||||||
|
resource = MockLoader.new(:alpine).load_resource('processes', '/the/biggest/program')
|
||||||
|
_(resource.entries.length).must_equal 1
|
||||||
|
_(resource.entries[0].to_h).must_equal({
|
||||||
|
label: nil,
|
||||||
|
pid: 83,
|
||||||
|
cpu: nil,
|
||||||
|
mem: nil,
|
||||||
|
vsz: 2726297,
|
||||||
|
rss: 1048576,
|
||||||
|
tty: '?',
|
||||||
|
stat: 'S',
|
||||||
|
start: nil,
|
||||||
|
time: '39:00',
|
||||||
|
user: 'tim',
|
||||||
|
command: '/the/biggest/program',
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
it 'command name matches with output (string)' do
|
it 'command name matches with output (string)' do
|
||||||
resource = MockLoader.new(:windows).load_resource('processes', 'winlogon.exe')
|
resource = MockLoader.new(:windows).load_resource('processes', 'winlogon.exe')
|
||||||
_(resource.to_s).must_equal 'Processes winlogon.exe'
|
_(resource.to_s).must_equal 'Processes winlogon.exe'
|
||||||
|
|
Loading…
Reference in a new issue