Commit 32f3d9ce authored by Vladimír Čunát's avatar Vladimír Čunát

Merge !767: policy.rpz(): watch for file changes by default

Original thread: !752
parents c3d7c487 9f1723e4
Pipeline #45106 failed with stages
in 5 minutes and 44 seconds
......@@ -11,6 +11,8 @@ Bugfixes
- hints module: allow configuring the TTL and change default from 0 to 5s
- policy module: policy.rpz() will watch the file for changes by default
- packaging: lua cqueues added to default dependencies where available
Knot Resolver 3.2.1 (2019-01-10)
......@@ -34,6 +34,7 @@ Depends:
libkres9 (= ${binary:Version}),
......@@ -59,6 +59,7 @@ BuildRequires: pkgconfig(lmdb)
BuildRequires: python3-sphinx
Requires: lua-socket-compat
Requires: lua-sec-compat
Requires: lua-cqueues-compat
Requires(pre): shadow-utils
%if 0%{?suse_version}
......@@ -227,10 +227,11 @@ Most properties (actions, filters) are described above.
Like suffix match, but you can also provide a common suffix of all matches for faster processing (nil otherwise).
This function is faster for small suffix tables (in the order of "hundreds").
.. function:: policy.rpz(action, path)
.. function:: policy.rpz(action, path, watch)
:param action: the default action for match in the zone; typically you want ``policy.DENY``
:param path: path to zone file | database
:param watch: boolean, if not false, the file will be reparsed and the ruleset reloaded on file change
Enforce RPZ_ rules. This can be used in conjunction with published blocklist feeds.
The RPZ_ operation is well described in this `Jan-Piet Mens's post`_,
......@@ -450,17 +450,61 @@ local function rpz_parse(action, path)
rules[name] = action_map[name_action]
-- Warn when NYI
if #name > 1 and not action_map[name_action] then
print(string.format('[ rpz ] %s:%d: unsupported policy action', path, tonumber(parser.line_counter)))
log('[poli] RPZ %s:%d: unsupported policy action', path, tonumber(parser.line_counter))
return rules
-- Split path into dirname and basename (like the shell utilities)
local function get_dir_and_file(path)
local dir, file = string.match(path, "(.*)/([^/]+)")
-- If regex doesn't match then path must be the file directly (i.e. doesn't contain '/')
-- This assumes that the file exists (rpz_parse() would fail if it doesn't)
if not dir and not file then
dir = '.'
file = path
return dir, file
-- RPZ policy set
-- Create RPZ from zone file
function policy.rpz(action, path)
-- Create RPZ from zone file and optionally watch the file for changes
function policy.rpz(action, path, watch)
local rules = rpz_parse(action, path)
if watch or true then
local has_notify, notify = pcall(require, 'cqueues.notify')
if has_notify then
local bit = require('bit')
local dir, file = get_dir_and_file(path)
local watcher = notify.opendir(dir)
watcher:add(file, bit.bxor(notify.CREATE, notify.MODIFY))
worker.coroutine(function ()
for _, name in watcher:changes() do
-- Limit to changes on file we're interested in
-- Watcher will also fire for changes to the directory itself
if name == file then
-- If the file changes then reparse and replace the existing ruleset
if verbose() then
log('[poli] RPZ reloading: ' .. name)
rules = rpz_parse(action, path)
elseif watch then -- explicitly requested and failed
error('[poli] lua-cqueues required to watch and reload RPZ file')
elseif verbose() then
log('[poli] lua-cqueues required to watch and reload RPZ file, continuing without watching')
return function(_, query)
local label = query:name()
local rule = rules[label]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment