Class: ScriptOutputParser
- Inherits:
-
Object
- Object
- ScriptOutputParser
- Defined in:
- lib/audit/lib/parser/script_output_parser.rb
Overview
This class parses the output generated by a sh script. Each output line is expected to start with the marker LINE_START and fields are also separated with SEPARATOR.
-
The first field is expected to be the name of the script part that generated the message.
-
The second field is expected to be a severity level defined in RuleSeverity (RuleSeverity).
-
The third field is the message type. Currently there are message types defined for program names (ProgramNameCommand), file attachment (AttachFileCommand), end of a check (CheckFinishedCommand), and key-value style data (DataCommand).
- Author
-
Jonas Zaddach, SecludIT (zaddach [at] eurecom [dot] fr)
Constant Summary collapse
- LINE_START =
marker used to identify beginning of a parseable line
"%%"
- SEPARATOR =
marker used to separate fields in the line
"%%"
- @@log =
Logger.
Logger.new(STDOUT)
- @@command_mapper =
Mapping of command names to commands. TODO: This should be moved into a factory class
{ AttachFileCommand::COMMAND => AttachFileCommand, MessageCommand::COMMAND => MessageCommand, ProgramNameCommand::COMMAND => ProgramNameCommand, CpeNameCommand::COMMAND => CpeNameCommand, CheckFinishedCommand::COMMAND => CheckFinishedCommand, ListeningPortCommand::COMMAND => ListeningPortCommand, DataCommand::COMMAND => DataCommand}
Instance Attribute Summary collapse
-
#attachment_dir ⇒ Object
readonly
Path string of the directory where attachments are stored.
-
#benchmark ⇒ Object
readonly
benchmark that is run on the remote host.
-
#check_results ⇒ Object
readonly
Results of the benchmark.
-
#connection ⇒ Object
readonly
connection that is used to connect to the remote host.
Instance Method Summary collapse
-
#consume_stderr(text) ⇒ Object
This method is called whenever data on stderr is encountered.
-
#consume_stdout(line) ⇒ Object
Called whenever a new line of output was received on the connection.
-
#initialize(options) ⇒ ScriptOutputParser
constructor
Create a new script output parser and set up the connection object that output is forwarded to this parser.
-
#on_check_completed(&block) ⇒ Object
Set a handler that is called whenever a check has completed.
-
#on_finished(&block) ⇒ Object
Set a handler that is called when the benchmark has completed.
-
#process_check_finished_command(cmd) ⇒ Object
Called whenever a CheckFinishedCommand was encountered.
-
#process_command(cmd) ⇒ Object
Called whenever a complete response line of a script has been read and the contained command was parsed successfully.
-
#process_unknown_check_id(line, check_id_string) ⇒ Object
Called whenever an unknown check id was encountered.
-
#process_unknown_output(line) ⇒ Object
Called whenever unknown output (that does not fit the specification, i.e. does not start with %%) is encountered on stdout.
-
#process_unknown_reply_command(line, check, severity, cmd_string) ⇒ Object
Called whenever an unknown reply command was encountered.
-
#progress ⇒ Object
Get the current progress of the benchmark execution as a float value between 0 and 1.
-
#remaining_time ⇒ Object
Get the estimated remaining time in seconds.
Constructor Details
#initialize(options) ⇒ ScriptOutputParser
Create a new script output parser and set up the connection object that output is forwarded to this parser.
-
benchmark Benchmark that generates the output to parse.
-
connection Connection to the remote host where the benchmark is executed.
-
attachment_dir A writable directory path as string where file attachments from AttachFileCommand are supposed to be stored.
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 65 def initialize() raise "Need parameter :benchmark" unless [:benchmark] raise "Need parameter :connection" unless [:connection] @connection = [:connection] @benchmark = [:benchmark] if [:attachment_dir] then @attachment_dir = [:attachment_dir][-1] == '/' ? [:attachment_dir][0 ... -1] : [:attachment_dir] if !File.exists?(@attachment_dir) || !File.writable?(@attachment_dir) then raise SecurityError, "attachment directory #{@attachment_dir} is not writable" end else @attachment_dir = nil end @@log.level = Logger::DEBUG @@log = [:logger] if [:logger] @check_results = {} @rule_results = {} @total_time_units = 0 @done_time_units = 0 @total_time_units = @benchmark.duration() @stdout_line_buffer = StdoutLineBuffer.new(connection) @stdout_line_buffer.on_line do |msg| consume_stdout(msg) end connection.on_stderr do|msg| consume_stderr(msg) end end |
Instance Attribute Details
#attachment_dir ⇒ Object (readonly)
Path string of the directory where attachments are stored.
56 57 58 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 56 def @attachment_dir end |
#benchmark ⇒ Object (readonly)
benchmark that is run on the remote host
50 51 52 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 50 def benchmark @benchmark end |
#check_results ⇒ Object (readonly)
Results of the benchmark. The results of each check are pushed into this array after the check has completed.
54 55 56 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 54 def check_results @check_results end |
#connection ⇒ Object (readonly)
connection that is used to connect to the remote host
48 49 50 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 48 def connection @connection end |
Instance Method Details
#consume_stderr(text) ⇒ Object
This method is called whenever data on stderr is encountered. Normally this should never happen, as all scripts are supposed to suppress output on stderr.
137 138 139 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 137 def consume_stderr(text) @@log.warn {"Unexpected line on stderr: '#{text}'. Verify that scripts are not broken."} end |
#consume_stdout(line) ⇒ Object
Called whenever a new line of output was received on the connection.
-
line The newly received output line.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 103 def consume_stdout(line) @@log.warn {"incomplete line '#{line}' on stdout, not terminated by \\n"} if line[-1] != "\n" if /^#{LINE_START}\s*([A-Za-z0-9_]+)\s*#{SEPARATOR}\s*([A-Za-z]+)\s*#{SEPARATOR}\s*([A-Za-z0-9_]+)\s*#{SEPARATOR}\s*(.*)$/.match(line) then #parse check id check = @benchmark.item_repository[$1] or process_unknown_check_id(line, $1) severity = RuleSeverity::parse($2) cmd_class = @@command_mapper[$3.upcase] if cmd_class.nil? then process_unknown_reply_command(line, check, severity, $3) else @@log.debug {"processing command #{cmd_class.name}: #{line}"} process_command(cmd_class.new(check, severity, $4.split(SEPARATOR))) end else process_unknown_output(line) end end |
#on_check_completed(&block) ⇒ Object
Set a handler that is called whenever a check has completed.
-
block The given block is supposed to accept one argument, that is a RuleResult instance of the just-finished check.
190 191 192 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 190 def on_check_completed(&block) @check_completed_handler = block end |
#on_finished(&block) ⇒ Object
Set a handler that is called when the benchmark has completed.
-
block The given block is supposed to accept two arguments, the first being the benchmark object, and the second an Array of RuleResult objects, one for each check completed.
198 199 200 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 198 def on_finished(&block) @finished_handler = block end |
#process_check_finished_command(cmd) ⇒ Object
Called whenever a CheckFinishedCommand was encountered.
157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 157 def process_check_finished_command(cmd) #calculate progress percentage check = cmd.result().check @done_time_units += check.duration @@log.info {"Check #{check.id} finished, progress is %.2f%" % [progress() * 100]} @rule_results[check.id] = RuleResult.new(check, @check_results[check]) @check_completed_handler.call(@rule_results[check.id]) unless @check_completed_handler.nil? #check if benchmark is finished @finished_handler.call(@benchmark, @rule_results) if @finished_handler && @done_time_units == @total_time_units end |
#process_command(cmd) ⇒ Object
Called whenever a complete response line of a script has been read and the contained command was parsed successfully.
143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 143 def process_command(cmd) cmd.process(self) result = cmd.result() @@log.debug {"cmd: #{cmd.class.name}, check_id: #{cmd.check.id}"} (@check_results[result.check] = [result] unless @check_results[result.check]) or @check_results[result.check] << result process_check_finished_command(cmd) if cmd.class == CheckFinishedCommand end |
#process_unknown_check_id(line, check_id_string) ⇒ Object
Called whenever an unknown check id was encountered.
178 179 180 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 178 def process_unknown_check_id(line, check_id_string) raise ParseException, "Script replied with unknown check id #{$1}" end |
#process_unknown_output(line) ⇒ Object
Called whenever unknown output (that does not fit the specification, i.e. does not start with %%) is encountered on stdout.
173 174 175 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 173 def process_unknown_output(line) @@log.warn {"Unexpected output line on stdout: '#{line}'. Verify that the scripts are not broken."} end |
#process_unknown_reply_command(line, check, severity, cmd_string) ⇒ Object
Called whenever an unknown reply command was encountered.
183 184 185 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 183 def process_unknown_reply_command(line, check, severity, cmd_string) @@log.warn {"Command not found: '#{line}'."} end |
#progress ⇒ Object
Get the current progress of the benchmark execution as a float value between 0 and 1.
124 125 126 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 124 def progress() return 1.0 * @done_time_units / @total_time_units end |
#remaining_time ⇒ Object
Get the estimated remaining time in seconds. This value is very inexact.
130 131 132 |
# File 'lib/audit/lib/parser/script_output_parser.rb', line 130 def remaining_time() return @total_time_units - @done_time_units end |