]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
module API+ABI: remove one level of indirection
authorVladimír Čunát <vladimir.cunat@nic.cz>
Thu, 14 Mar 2019 12:09:48 +0000 (13:09 +0100)
committerTomas Krizek <tomas.krizek@nic.cz>
Wed, 17 Apr 2019 16:44:29 +0000 (16:44 +0000)
... for layers and props.  This breaks C module API+ABI.

It seemed weird to repeatedly call a function that returns a pointer
to a structure in which we find the function we want to actually call.
We've never used changing these functions AFAIK, and the target
functions could easily be written to change their behavior instead
(i.e. move the indirection *inside* the function).

When breaking this, I also removed these two (_layers and _props)
from the dynamic symbols (to be) exported from the C modules.
They always pointed to memory belonging inside the module,
and they seem quite sensible to be set up by the _init symbol instead.

17 files changed:
daemon/engine.c
daemon/ffimodule.c
lib/layer/cache.c
lib/layer/iterate.c
lib/layer/validate.c
lib/module.c
lib/module.h
lib/resolve.c
lib/utils.c
modules/README.rst
modules/bogus_log/bogus_log.c
modules/cookies/cookies.c
modules/dnstap/dnstap.c
modules/edns_keepalive/edns_keepalive.c
modules/hints/hints.c
modules/nsid/nsid.c
modules/stats/stats.c

index 2eb57684eb14b3a7f0c2ff013bd48fbdd9162c2f..9ba6d2b63691aa0c1fb2ea0cee09eaeac147f283 100644 (file)
@@ -662,13 +662,13 @@ int engine_init(struct engine *engine, knot_mm_t *pool)
        return ret;
 }
 
+/** Unregister a (found) module */
 static void engine_unload(struct engine *engine, struct kr_module *module)
 {
-       /* Unregister module */
        auto_free char *name = strdup(module->name);
        kr_module_unload(module);
        /* Clear in Lua world, but not for embedded modules ('cache' in particular). */
-       if (name && !kr_module_embedded(name)) {
+       if (name && !kr_module_get_embedded(name)) {
                lua_pushnil(engine->L);
                lua_setglobal(engine->L, name);
        }
@@ -821,8 +821,7 @@ static int register_properties(struct engine *engine, struct kr_module *module)
                REGISTER_MODULE_CALL(engine->L, module, module->config, "config");
        }
 
-       const struct kr_prop *p = module->props == NULL ? NULL : module->props();
-       for (; p && p->name; ++p) {
+       for (const struct kr_prop *p = module->props; p && p->name; ++p) {
                if (p->cb != NULL) {
                        REGISTER_MODULE_CALL(engine->L, module, p->cb, p->name);
                }
index 69fc5880556e3412e7ffe7622293d8de2a71ec8a..496be6fae68493c92039513ba53c2a7078477676 100644 (file)
@@ -246,14 +246,6 @@ static kr_layer_api_t *l_ffi_layer_create(lua_State *L, struct kr_module *module
        return api;
 }
 
-/** @internal Retrieve C layer api wrapper. */
-static const kr_layer_api_t *l_ffi_layer(struct kr_module *module)
-{
-       if (module) {
-               return (const kr_layer_api_t *)module->data;
-       }
-       return NULL;
-}
 #undef LAYER_REGISTER
 
 int ffimodule_register_lua(struct engine *engine, struct kr_module *module, const char *name)
@@ -278,8 +270,9 @@ int ffimodule_register_lua(struct engine *engine, struct kr_module *module, cons
        /* Bake layer API if defined in module */
        lua_getfield(L, -1, "layer");
        if (!lua_isnil(L, -1)) {
-               module->layer = &l_ffi_layer;
-               module->data = l_ffi_layer_create(L, module);
+               module->layer = l_ffi_layer_create(L, module);
+               /* most likely not needed, but compatibility for now */
+               module->data = (void *)module->layer;
        }
        module->lib = L;
        lua_pop(L, 2); /* Clear the layer + module global */
index c7bbc1ab6fa6ea2c9daf1025b7b45928cb9857ee..b1471bc50076401b9ee8347e015097c66fb03b0e 100644 (file)
 #include "lib/cache/api.h"
 
 /** Module implementation. */
-const kr_layer_api_t *cache_layer(struct kr_module *module)
+int cache_init(struct kr_module *self)
 {
-       static const kr_layer_api_t _layer = {
+       static const kr_layer_api_t layer = {
                .produce = &cache_peek,
                .consume = &cache_stash,
        };
-
-       return &_layer;
+       self->layer = &layer;
+       return kr_ok();
 }
 
-KR_MODULE_EXPORT(cache)
+KR_MODULE_EXPORT(cache) /* useless for builtin module, but let's be consistent */
+
index dbc3761a2ab87a032b2b778b0b2e2bc580447d41..be0fb184455c3f47c1dbdc94a2dde3a6a0561b9a 100644 (file)
@@ -1111,17 +1111,18 @@ static int resolve(kr_layer_t *ctx, knot_pkt_t *pkt)
 }
 
 /** Module implementation. */
-const kr_layer_api_t *iterate_layer(struct kr_module *module)
+int iterate_init(struct kr_module *self)
 {
-       static const kr_layer_api_t _layer = {
+       static const kr_layer_api_t layer = {
                .begin = &begin,
                .reset = &reset,
                .consume = &resolve,
                .produce = &prepare_query
        };
-       return &_layer;
+       self->layer = &layer;
+       return kr_ok();
 }
 
-KR_MODULE_EXPORT(iterate)
+KR_MODULE_EXPORT(iterate) /* useless for builtin module, but let's be consistent */
 
 #undef VERBOSE_MSG
index 16e339602111ea786d8946fa8c9d0576a02ca17d..59634bc30f923880837233459209183533a80385 100644 (file)
@@ -1116,21 +1116,17 @@ static int validate(kr_layer_t *ctx, knot_pkt_t *pkt)
        VERBOSE_MSG(qry, "<= answer valid, OK\n");
        return KR_STATE_DONE;
 }
+
 /** Module implementation. */
-const kr_layer_api_t *validate_layer(struct kr_module *module)
+int validate_init(struct kr_module *self)
 {
-       static const kr_layer_api_t _layer = {
+       static const kr_layer_api_t layer = {
                .consume = &validate,
        };
-       /* Store module reference */
-       return &_layer;
-}
-
-int validate_init(struct kr_module *module)
-{
+       self->layer = &layer;
        return kr_ok();
 }
 
-KR_MODULE_EXPORT(validate)
+KR_MODULE_EXPORT(validate) /* useless for builtin module, but let's be consistent */
 
 #undef VERBOSE_MSG
index e003707087be3c1472fd72bebd656820a46289c3..c88bd245291c91a95558e7f5a1ef74dc5d5e201d 100644 (file)
 #include "lib/utils.h"
 #include "lib/module.h"
 
-/* List of embedded modules */
-const kr_layer_api_t *iterate_layer(struct kr_module *module);
-const kr_layer_api_t *validate_layer(struct kr_module *module);
-const kr_layer_api_t *cache_layer(struct kr_module *module);
-static const struct kr_module embedded_modules[] = {
-       { "iterate",  NULL, NULL, NULL, iterate_layer,  NULL, NULL, NULL },
-       { "validate", NULL, NULL, NULL, validate_layer, NULL, NULL, NULL },
-       { "cache",    NULL, NULL, NULL, cache_layer,    NULL, NULL, NULL },
-};
 
+/* List of embedded modules.  These aren't (un)loaded. */
+int iterate_init(struct kr_module *self);
+int validate_init(struct kr_module *self);
+int cache_init(struct kr_module *self);
+kr_module_init_cb kr_module_get_embedded(const char *name)
+{
+       if (strcmp(name, "iterate") == 0)
+               return iterate_init;
+       if (strcmp(name, "validate") == 0)
+               return validate_init;
+       if (strcmp(name, "cache") == 0)
+               return cache_init;
+       return NULL;
+}
 
 /** Load prefixed symbol. */
 static void *load_symbol(void *lib, const char *prefix, const char *name)
@@ -60,30 +65,16 @@ static int load_library(struct kr_module *module, const char *name, const char *
        return kr_error(ENOENT);
 }
 
-const struct kr_module * kr_module_embedded(const char *name)
-{
-       for (unsigned i = 0; i < sizeof(embedded_modules)/sizeof(embedded_modules[0]); ++i) {
-               if (strcmp(name, embedded_modules[i].name) == 0)
-                       return embedded_modules + i;
-       }
-       return NULL;
-}
-
 /** Load C module symbols. */
 static int load_sym_c(struct kr_module *module, uint32_t api_required)
 {
+       module->init = kr_module_get_embedded(module->name);
+       if (module->init) {
+               return kr_ok();
+       }
        #pragma GCC diagnostic push
        #pragma GCC diagnostic ignored "-Wpedantic" /* casts after load_symbol() */
        /* Check if it's embedded first */
-       const struct kr_module *embedded = kr_module_embedded(module->name);
-       if (embedded) {
-               module->init = embedded->init;
-               module->deinit = embedded->deinit;
-               module->config = embedded->config;
-               module->layer = embedded->layer;
-               module->props = embedded->props;
-               return kr_ok();
-       }
        /* Load dynamic library module */
        auto_free char *m_prefix = kr_strcatdup(2, module->name, "_");
 
@@ -102,9 +93,14 @@ static int load_sym_c(struct kr_module *module, uint32_t api_required)
        ML(init);
        ML(deinit);
        ML(config);
-       ML(layer);
-       ML(props);
        #undef ML
+       if (load_symbol(module->lib, m_prefix, "layer")
+           || load_symbol(module->lib, m_prefix, "props")) {
+               /* In case someone re-compiled against new kresd
+                * but haven't actually changed the symbols. */
+               kr_log_error("[system] module %s needs to change API.\n", module->name);
+               return kr_error(ENOTSUP);
+       }
 
        return kr_ok();
        #pragma GCC diagnostic pop
index 295c1f9bac7b8193f4343706feec2453149857ac..63f4cb8012edccab956f306892dcab4cc5686970 100644 (file)
@@ -35,7 +35,7 @@ struct kr_prop;
  */
 #define KR_MODULE_EXPORT(module) \
     KR_EXPORT uint32_t module ## _api() { return KR_MODULE_API; }
-#define KR_MODULE_API ((uint32_t) 0x20180401)
+#define KR_MODULE_API ((uint32_t) 0x20190314)
 
 typedef uint32_t (module_api_cb)(void);
 
@@ -55,10 +55,10 @@ struct kr_module {
        int (*deinit)(struct kr_module *self);
        /** Configure with encoded JSON (NULL if missing).  @return error code. */
        int (*config)(struct kr_module *self, const char *input);
-       /** Get a pointer to packet processing API specs.  See docs on that type. */
-       const kr_layer_api_t * (*layer)(struct kr_module *self);
-       /** Get a pointer to list of properties, terminated by { NULL, NULL, NULL }. */
-       const struct kr_prop * (*props)(void);
+       /** Packet processing API specs.  May be NULL.  See docs on that type. */
+       const kr_layer_api_t *layer;
+       /** List of properties.  May be NULL.  Terminated by { NULL, NULL, NULL }. */
+       const struct kr_prop *props;
 
        void *lib;      /**< Shared library handle or RTLD_DEFAULT */
        void *data;     /**< Custom data context. */
@@ -103,9 +103,10 @@ int kr_module_load(struct kr_module *module, const char *name, const char *path)
 KR_EXPORT
 void kr_module_unload(struct kr_module *module);
 
+typedef int (*kr_module_init_cb)(struct kr_module *);
 /**
- * Get embedded module prototype by name (or NULL).
+ * Get embedded module's init function by name (or NULL).
  */
 KR_EXPORT
-const struct kr_module * kr_module_embedded(const char *name);
+kr_module_init_cb kr_module_get_embedded(const char *name);
 
index 2493bb1a38c3f42563ac7fd25d8a6607c34f6ad1..2d97e900e42e45c3c6e787fbbfb40f51e0490955 100644 (file)
@@ -108,7 +108,7 @@ static int answer_finalize_yield(kr_layer_t *ctx) { return kr_ok(); }
        for (size_t i = (from); i < (r)->ctx->modules->len; ++i) { \
                struct kr_module *mod = (r)->ctx->modules->at[i]; \
                if (mod->layer) { \
-                       struct kr_layer layer = {.state = (r)->state, .api = mod->layer(mod), .req = (r)}; \
+                       struct kr_layer layer = {.state = (r)->state, .api = mod->layer, .req = (r)}; \
                        if (layer.api && layer.api->func) { \
                                (r)->state = layer.api->func(&layer, ##__VA_ARGS__); \
                                if ((r)->state == KR_STATE_YIELD) { \
@@ -127,8 +127,7 @@ static int answer_finalize_yield(kr_layer_t *ctx) { return kr_ok(); }
 static inline size_t layer_id(struct kr_request *req, const struct kr_layer_api *api) {
        module_array_t *modules = req->ctx->modules;
        for (size_t i = 0; i < modules->len; ++i) {
-               struct kr_module *mod = modules->at[i];
-               if (mod->layer && mod->layer(mod) == api) {
+               if (modules->at[i]->layer == api) {
                        return i;
                }
        }
index 8f4aee6ddc902fca91c627da4bf9cc066706d46d..ee13a4a85603fad3673d1e8e2b3ee4451fa338f7 100644 (file)
@@ -766,7 +766,7 @@ static char *callprop(struct kr_module *module, const char *prop, const char *in
        if (!module || !module->props || !prop) {
                return NULL;
        }
-       for (const struct kr_prop *p = module->props(); p && p->name; ++p) {
+       for (const struct kr_prop *p = module->props; p && p->name; ++p) {
                if (p->cb != NULL && strcmp(p->name, prop) == 0) {
                        return p->cb(env, module, input);
                }
index b15f3ca3513a6fc4363aef2760a98be1e07d3fd0..d0ee84d557a03c357536d87558075488761245ae 100644 (file)
@@ -16,6 +16,8 @@ Currently modules written in C and LuaJIT are supported.
 The anatomy of an extension
 ===========================
 
+FIXME: review and fix.
+
 A module is a shared object or script defining specific functions, here's an overview.
 
 *Note* |---| the :ref:`Modules <lib_api_modules>` header documents the module loading and API.
index 98c0fc919953a7d53bab14629f2292a9ccfdc5cd..09f4d68288873793e5a4950eb1dbfeed49a4b433 100644 (file)
@@ -105,6 +105,18 @@ static char* dump_frequent(void *env, struct kr_module *module, const char *args
 KR_EXPORT
 int bogus_log_init(struct kr_module *module)
 {
+       static kr_layer_api_t layer = {
+               .consume = &consume,
+       };
+       layer.data = module;
+       module->layer = &layer;
+
+       static const struct kr_prop props[] = {
+               { &dump_frequent, "frequent", "List most frequent queries.", },
+               { NULL, NULL, NULL }
+       };
+       module->props = props;
+
        struct stat_data *data = malloc(sizeof(*data));
        if (!data) {
                return kr_error(ENOMEM);
@@ -126,25 +138,4 @@ int bogus_log_deinit(struct kr_module *module)
        return kr_ok();
 }
 
-
-KR_EXPORT
-const kr_layer_api_t *bogus_log_layer(struct kr_module *module)
-{
-       static kr_layer_api_t _layer = {
-               .consume = &consume,
-       };
-       _layer.data = module;
-       return &_layer;
-}
-
-KR_EXPORT
-struct kr_prop *bogus_log_props(void)
-{
-       static struct kr_prop prop_list[] = {
-               { &dump_frequent, "frequent", "List most frequent queries.", },
-               { NULL, NULL, NULL }
-       };
-       return prop_list;
-}
-
 KR_MODULE_EXPORT(bogus_log)
index 6a1db4f249636013f5e43e4353616b515d7e9a1e..c7a350c3a9d418dd80adcb3325a67a12167854fb 100644 (file)
@@ -47,6 +47,22 @@ static char *cookies_config(void *env, struct kr_module *module,
 KR_EXPORT
 int cookies_init(struct kr_module *module)
 {
+       /* The function answer_finalize() in resolver is called before any
+        * .finish callback. Therefore this layer does not use it. */
+       static kr_layer_api_t layer = {
+               .begin = &check_request,
+               .consume = &check_response
+       };
+       /* Store module reference */
+       layer.data = module;
+       module->layer = &layer;
+
+       static const struct kr_prop props[] = {
+           { &cookies_config, "config", "Empty value to return current configuration.", },
+           { NULL, NULL, NULL }
+       };
+       module->props = props;
+
        struct engine *engine = module->data;
 
        struct kr_cookie_ctx *cookie_ctx = &engine->resolver.cookie_ctx;
@@ -72,29 +88,4 @@ int cookies_deinit(struct kr_module *module)
        return kr_ok();
 }
 
-KR_EXPORT
-const kr_layer_api_t *cookies_layer(struct kr_module *module)
-{
-       /* The function answer_finalize() in resolver is called before any
-        * .finish callback. Therefore this layer does not use it. */
-
-       static kr_layer_api_t _layer = {
-               .begin = &check_request,
-               .consume = &check_response
-       };
-       /* Store module reference */
-       _layer.data = module;
-       return &_layer;
-}
-
-KR_EXPORT
-struct kr_prop *cookies_props(void)
-{
-       static struct kr_prop prop_list[] = {
-           { &cookies_config, "config", "Empty value to return current configuration.", },
-           { NULL, NULL, NULL }
-       };
-       return prop_list;
-}
-
 KR_MODULE_EXPORT(cookies)
index 0918426044c6ec75728b76da42a1b2feb2936c06..57c22c1851f8a5adf7a37900c88c53cf752433f7 100644 (file)
@@ -211,6 +211,13 @@ static int dnstap_log(kr_layer_t *ctx) {
 
 KR_EXPORT
 int dnstap_init(struct kr_module *module) {
+       static kr_layer_api_t layer = {
+               .finish = &dnstap_log,
+       };
+       /* Store module reference */
+       layer.data = module;
+       module->layer = &layer;
+
        /* allocated memory for internal data */
        struct dnstap_data *data = malloc(sizeof(*data));
        if (!data) {
@@ -368,15 +375,5 @@ int dnstap_config(struct kr_module *module, const char *conf) {
        return kr_ok();
 }
 
-KR_EXPORT
-const kr_layer_api_t *dnstap_layer(struct kr_module *module) {
-       static kr_layer_api_t _layer = {
-               .finish = &dnstap_log,
-       };
-       /* Store module reference */
-       _layer.data = module;
-       return &_layer;
-}
-
 KR_MODULE_EXPORT(dnstap)
 
index 99c0d9661eee29b8e1b0ab8c43ba52838dcb468d..1a598638aa7e04712b8b856b2018b4510b22c725 100644 (file)
@@ -62,15 +62,13 @@ static int edns_keepalive_finalize(kr_layer_t *ctx)
        return ctx->state;
 }
 
-KR_EXPORT
-const kr_layer_api_t *edns_keepalive_layer(struct kr_module *module)
+KR_EXPORT int edns_keeapalive_init(struct kr_module *self)
 {
-       static kr_layer_api_t _layer = {
+       static const kr_layer_api_t layer = {
                .answer_finalize = &edns_keepalive_finalize,
        };
-       /* Store module reference */
-       _layer.data = module;
-       return &_layer;
+       self->layer = &layer;
+       return kr_ok();
 }
 
 KR_MODULE_EXPORT(edns_keepalive)
index 1f154ae7ff2eed3a308d7b9f3ef6a1ce470582a3..613dcc651542a834f9b2b55b2c0cee825dd11c5a 100644 (file)
@@ -600,26 +600,30 @@ static char* hint_ttl(void *env, struct kr_module *module, const char *args)
        return result;
 }
 
-/*
- * Module implementation.
- */
-
+/** Basic initialization: get a memory pool, etc. */
 KR_EXPORT
-const kr_layer_api_t *hints_layer(struct kr_module *module)
+int hints_init(struct kr_module *module)
 {
-       static kr_layer_api_t _layer = {
+       static kr_layer_api_t layer = {
                .produce = &query,
        };
        /* Store module reference */
-       _layer.data = module;
-       return &_layer;
-}
+       layer.data = module;
+       module->layer = &layer;
 
+       static const struct kr_prop props[] = {
+           { &hint_set,    "set", "Set {name, address} hint.", },
+           { &hint_del,    "del", "Delete one {name, address} hint or all addresses for the name.", },
+           { &hint_get,    "get", "Retrieve hint for given name.", },
+           { &hint_ttl,    "ttl", "Set/get TTL used for the hints.", },
+           { &hint_add_hosts, "add_hosts", "Load a file with hosts-like formatting and add contents into hints.", },
+           { &hint_root,   "root", "Replace root hints set (empty value to return current list).", },
+           { &hint_root_file, "root_file", "Replace root hints set from a zonefile.", },
+           { &hint_use_nodata, "use_nodata", "Synthesise NODATA if name matches, but type doesn't.  True by default.", },
+           { NULL, NULL, NULL }
+       };
+       module->props = props;
 
-/** Basic initialization: get a memory pool, etc. */
-KR_EXPORT
-int hints_init(struct kr_module *module)
-{
        /* Create pool and copy itself */
        knot_mm_t _pool = {
                .ctx = mp_new(4096),
@@ -678,23 +682,6 @@ int hints_config(struct kr_module *module, const char *conf)
        return kr_ok();
 }
 
-KR_EXPORT
-struct kr_prop *hints_props(void)
-{
-       static struct kr_prop prop_list[] = {
-           { &hint_set,    "set", "Set {name, address} hint.", },
-           { &hint_del,    "del", "Delete one {name, address} hint or all addresses for the name.", },
-           { &hint_get,    "get", "Retrieve hint for given name.", },
-           { &hint_ttl,    "ttl", "Set/get TTL used for the hints.", },
-           { &hint_add_hosts, "add_hosts", "Load a file with hosts-like formatting and add contents into hints.", },
-           { &hint_root,   "root", "Replace root hints set (empty value to return current list).", },
-           { &hint_root_file, "root_file", "Replace root hints set from a zonefile.", },
-           { &hint_use_nodata, "use_nodata", "Synthesise NODATA if name matches, but type doesn't.  True by default.", },
-           { NULL, NULL, NULL }
-       };
-       return prop_list;
-}
-
 KR_MODULE_EXPORT(hints)
 
 #undef VERBOSE_MSG
index fef6ecd3ea63c285063df38a00124ff5fe960e32..f5c8e627341d73a75e0b23062ec25c77063c8ba0 100644 (file)
@@ -57,26 +57,6 @@ static int nsid_finalize(kr_layer_t *ctx) {
        return ctx->state;
 }
 
-KR_EXPORT
-const kr_layer_api_t *nsid_layer(struct kr_module *module)
-{
-       static kr_layer_api_t _layer = {
-               .answer_finalize = &nsid_finalize,
-       };
-       _layer.data = module;
-       return &_layer;
-}
-
-KR_EXPORT
-int nsid_init(struct kr_module *module) {
-       struct nsid_config *config = calloc(1, sizeof(struct nsid_config));
-       if (config == NULL)
-               return kr_error(ENOMEM);
-
-       module->data = config;
-       return kr_ok();
-}
-
 static char* nsid_name(void *env, struct kr_module *module, const char *args)
 {
        struct engine *engine = env;
@@ -99,13 +79,25 @@ static char* nsid_name(void *env, struct kr_module *module, const char *args)
 }
 
 KR_EXPORT
-struct kr_prop *nsid_props(void)
-{
-       static struct kr_prop prop_list[] = {
+int nsid_init(struct kr_module *module) {
+       static kr_layer_api_t layer = {
+               .answer_finalize = &nsid_finalize,
+       };
+       layer.data = module;
+       module->layer = &layer;
+
+       static const struct kr_prop props[] = {
            { &nsid_name, "name", "Get or set local NSID value" },
            { NULL, NULL, NULL }
        };
-       return prop_list;
+       module->props = props;
+
+       struct nsid_config *config = calloc(1, sizeof(struct nsid_config));
+       if (config == NULL)
+               return kr_error(ENOMEM);
+
+       module->data = config;
+       return kr_ok();
 }
 
 KR_EXPORT
index 5d377632c8721bd783e4f4acc4abde88c77bd3cd..add632ebab9277ddfb6ffe189260dbfd3a7f7617 100644 (file)
@@ -460,26 +460,29 @@ static char* dump_upstreams(void *env, struct kr_module *module, const char *arg
        return ret;
 }
 
-/*
- * Module implementation.
- */
-
 KR_EXPORT
-const kr_layer_api_t *stats_layer(struct kr_module *module)
+int stats_init(struct kr_module *module)
 {
-       static kr_layer_api_t _layer = {
+       static kr_layer_api_t layer = {
                .consume = &collect_rtt,
                .finish = &collect,
                .begin = &collect_transport,
        };
        /* Store module reference */
-       _layer.data = module;
-       return &_layer;
-}
+       layer.data = module;
+       module->layer = &layer;
+
+       static const struct kr_prop props[] = {
+           { &stats_set,     "set", "Set {key, val} metrics.", },
+           { &stats_get,     "get", "Get metrics for given key.", },
+           { &stats_list,    "list", "List observed metrics.", },
+           { &dump_frequent, "frequent", "List most frequent queries.", },
+           { &clear_frequent,"clear_frequent", "Clear frequent queries log.", },
+           { &dump_upstreams,  "upstreams", "List recently seen authoritatives.", },
+           { NULL, NULL, NULL }
+       };
+       module->props = props;
 
-KR_EXPORT
-int stats_init(struct kr_module *module)
-{
        struct stat_data *data = malloc(sizeof(*data));
        if (!data) {
                return kr_error(ENOMEM);
@@ -513,21 +516,6 @@ int stats_deinit(struct kr_module *module)
        return kr_ok();
 }
 
-KR_EXPORT
-struct kr_prop *stats_props(void)
-{
-       static struct kr_prop prop_list[] = {
-           { &stats_set,     "set", "Set {key, val} metrics.", },
-           { &stats_get,     "get", "Get metrics for given key.", },
-           { &stats_list,    "list", "List observed metrics.", },
-           { &dump_frequent, "frequent", "List most frequent queries.", },
-           { &clear_frequent,"clear_frequent", "Clear frequent queries log.", },
-           { &dump_upstreams,  "upstreams", "List recently seen authoritatives.", },
-           { NULL, NULL, NULL }
-       };
-       return prop_list;
-}
-
 KR_MODULE_EXPORT(stats)
 
 #undef VERBOSE_MSG