123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- --[[
- Copyright (c) 2022, Vsevolod Stakhov <vsevolod@rspamd.com>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ]]--
-
- local argparse = require "argparse"
-
-
- -- Define command line options
- local parser = argparse()
- :name "rspamadm grep"
- :description "Search for patterns in rspamd logs"
- :help_description_margin(30)
- parser:mutex(
- parser:option "-s --string"
- :description('Plain string to search (case-insensitive)')
- :argname "<str>",
- parser:option "-p --pattern"
- :description('Pattern to search for (regex)')
- :argname "<re>"
- )
- parser:flag "-l --lua"
- :description('Use Lua patterns in string search')
-
- parser:argument "input":args "*"
- :description('Process specified inputs')
- :default("stdin")
- parser:flag "-S --sensitive"
- :description('Enable case-sensitivity in string search')
- parser:flag "-o --orphans"
- :description('Print orphaned logs')
- parser:flag "-P --partial"
- :description('Print partial logs')
-
- local function handler(args)
-
- local rspamd_regexp = require 'rspamd_regexp'
- local res = parser:parse(args)
-
- if not res['string'] and not res['pattern'] then
- parser:error('string or pattern options must be specified')
- end
-
- if res['string'] and res['pattern'] then
- parser:error('string and pattern options are mutually exclusive')
- end
-
- local buffer = {}
- local matches = {}
-
- local pattern = res['pattern']
- local re
- if pattern then
- re = rspamd_regexp.create(pattern)
- if not re then
- io.stderr:write("Couldn't compile regex: " .. pattern .. '\n')
- os.exit(1)
- end
- end
-
- local plainm = true
- if res['lua'] then
- plainm = false
- end
- local orphans = res['orphans']
- local search_str = res['string']
- local sensitive = res['sensitive']
- local partial = res['partial']
- if search_str and not sensitive then
- search_str = string.lower(search_str)
- end
- local inputs = res['input'] or { 'stdin' }
-
- for _, n in ipairs(inputs) do
- local h, err
- if string.match(n, '%.xz$') then
- h, err = io.popen('xzcat ' .. n, 'r')
- elseif string.match(n, '%.bz2$') then
- h, err = io.popen('bzcat ' .. n, 'r')
- elseif string.match(n, '%.gz$') then
- h, err = io.popen('zcat ' .. n, 'r')
- elseif string.match(n, '%.zst$') then
- h, err = io.popen('zstdcat ' .. n, 'r')
- elseif n == 'stdin' then
- h = io.input()
- else
- h, err = io.open(n, 'r')
- end
- if not h then
- if err then
- io.stderr:write("Couldn't open file (" .. n .. '): ' .. err .. '\n')
- else
- io.stderr:write("Couldn't open file (" .. n .. '): no error\n')
- end
- else
- for line in h:lines() do
- local hash = string.match(line, '<(%x+)>')
- local already_matching = false
- if hash then
- if matches[hash] then
- table.insert(matches[hash], line)
- already_matching = true
- else
- if buffer[hash] then
- table.insert(buffer[hash], line)
- else
- buffer[hash] = { line }
- end
- end
- end
- local ismatch = false
- if re then
- ismatch = re:match(line)
- elseif sensitive and search_str then
- ismatch = string.find(line, search_str, 1, plainm)
- elseif search_str then
- local lwr = string.lower(line)
- ismatch = string.find(lwr, search_str, 1, plainm)
- end
- if ismatch then
- if not hash then
- if orphans then
- print('*** orphaned ***')
- print(line)
- print()
- end
- elseif not already_matching then
- matches[hash] = buffer[hash]
- end
- end
- local is_end = string.match(line, '<%x+>; task; rspamd_protocol_http_reply:')
- if is_end then
- buffer[hash] = nil
- if matches[hash] then
- for _, v in ipairs(matches[hash]) do
- print(v)
- end
- print()
- matches[hash] = nil
- end
- end
- end
- if partial then
- for k, v in pairs(matches) do
- print('*** partial ***')
- for _, vv in ipairs(v) do
- print(vv)
- end
- print()
- matches[k] = nil
- end
- else
- matches = {}
- end
- end
- end
- end
-
- return {
- handler = handler,
- description = parser._description,
- name = 'grep'
- }
|