[tarantool-patches] [PATCH v1 1/1] box: support reload whole module

  • From: Kirill Shcherbatov <kshcherbatov@xxxxxxxxxxxxx>
  • To: tarantool-patches@xxxxxxxxxxxxx
  • Date: Mon, 9 Jul 2018 19:54:22 +0300

@TarantoolBot document
Title: fixed module reload
There was a bug in tarantool documentation:
https://tarantool.io/en/doc/1.7/book/box/
box_schema/#lua-function.box.schema.func.reload
Now it is allowed to reload all functions in loadable
module via one method.
box.schema.func.reload("utils")    -- ok since now

Global reload is still unsupported because it seems
to be useless.
box.schema.func.reload()           -- invalid!

Closes #2946.
---
https://github.com/tarantool/tarantool/compare/kshch/gh-2946-module-reload
https://github.com/tarantool/tarantool/issues/2946

 src/box/call.c                | 37 ++++++++++++++++---------------------
 src/box/call.h                | 18 ++++++++++++++++++
 src/box/func.c                | 21 ++++++++++++++++++---
 src/box/func.h                | 15 +++++++++++++++
 test/box/func_reload.result   |  7 +++++++
 test/box/func_reload.test.lua |  2 ++
 6 files changed, 76 insertions(+), 24 deletions(-)

diff --git a/src/box/call.c b/src/box/call.c
index 438be19..dfe15dd 100644
--- a/src/box/call.c
+++ b/src/box/call.c
@@ -42,20 +42,8 @@
 #include "rmean.h"
 #include "small/obuf.h"
 
-/**
- * Find a function by name and check "EXECUTE" permissions.
- *
- * @param name function name
- * @param name_len length of @a name
- * @param[out] funcp function object
- * Sic: *pfunc == NULL means that perhaps the user has a global
- * "EXECUTE" privilege, so no specific grant to a function.
- *
- * @retval -1 on access denied
- * @retval  0 on success
- */
-static inline int
-access_check_func(const char *name, uint32_t name_len, struct func **funcp)
+int
+box_func_check_access(const char *name, uint32_t name_len, struct func **funcp)
 {
        struct func *func = func_by_name(name, name_len);
        struct credentials *credentials = effective_user();
@@ -136,17 +124,24 @@ box_func_reload(const char *name)
 {
        size_t name_len = strlen(name);
        struct func *func = NULL;
-       if ((access_check_func(name, name_len, &func)) != 0)
+       if ((box_func_check_access(name, name_len, &func)) != 0)
                return -1;
        if (func == NULL) {
+               /* Try to interpret name as a module name. */
+               struct module *module;
+               if (module_reload(name, name + name_len, &module, true) == 0 &&
+                   module != NULL)
+                       return 0;
                diag_set(ClientError, ER_NO_SUCH_FUNCTION, name);
                return -1;
+       } else {
+               /* Such function exists. Try to reload it. */
+               if (func->def->language != FUNC_LANGUAGE_C)
+                       return 0; /* Nothing to do */
+               if (func_reload(func) == 0)
+                       return 0;
+               return -1;
        }
-       if (func->def->language != FUNC_LANGUAGE_C || func->func == NULL)
-               return 0; /* Nothing to do */
-       if (func_reload(func) == 0)
-               return 0;
-       return -1;
 }
 
 int
@@ -165,7 +160,7 @@ box_process_call(struct call_request *request, struct port 
*port)
         * Sic: func == NULL means that perhaps the user has a global
         * "EXECUTE" privilege, so no specific grant to a function.
         */
-       if (access_check_func(name, name_len, &func) != 0)
+       if (box_func_check_access(name, name_len, &func) != 0)
                return -1; /* permission denied */
 
        /**
diff --git a/src/box/call.h b/src/box/call.h
index eabba69..55cf925 100644
--- a/src/box/call.h
+++ b/src/box/call.h
@@ -35,8 +35,11 @@
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+#include <inttypes.h>
+
 struct port;
 struct call_request;
+struct func;
 
 struct box_function_ctx {
        struct port *port;
@@ -51,6 +54,21 @@ box_process_call(struct call_request *request, struct port 
*port);
 int
 box_process_eval(struct call_request *request, struct port *port);
 
+/**
+ * Find a function by name and check "EXECUTE" permissions.
+ *
+ * @param name function name
+ * @param name_len length of @a name
+ * @param[out] funcp function object
+ * Sic: *pfunc == NULL means that perhaps the user has a global
+ * "EXECUTE" privilege, so no specific grant to a function.
+ *
+ * @retval -1 on access denied.
+ * @retval  0 on success.
+ */
+int
+box_func_check_access(const char *name, uint32_t name_len, struct func 
**funcp);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/src/box/func.c b/src/box/func.c
index dfbc5f3..dfab5d0 100644
--- a/src/box/func.c
+++ b/src/box/func.c
@@ -34,6 +34,7 @@
 #include "lua/utils.h"
 #include "error.h"
 #include "diag.h"
+#include "call.h"
 #include <dlfcn.h>
 
 /**
@@ -302,7 +303,8 @@ module_sym(struct module *module, const char *name)
  * Reload a dso.
  */
 int
-module_reload(const char *package, const char *package_end, struct module 
**module)
+module_reload(const char *package, const char *package_end,
+             struct module **module, bool check_access)
 {
        struct module *old_module = module_cache_find(package, package_end);
        if (old_module == NULL) {
@@ -318,7 +320,19 @@ module_reload(const char *package, const char 
*package_end, struct module **modu
        struct func *func, *tmp_func;
        rlist_foreach_entry_safe(func, &old_module->funcs, item, tmp_func) {
                struct func_name name;
-               func_split_name(func->def->name, &name);
+               const char *func_name = func->def->name;
+               func_split_name(func_name, &name);
+
+               /*
+                * Allow to reload only functions that belongs to
+                * current user. Skip other.
+                */
+               struct func *dummy;
+               if (check_access &&
+                       box_func_check_access(func_name, strlen(func_name),
+                                             &dummy) != 0)
+                       continue;
+
                func->func = module_sym(new_module, name.sym);
                if (func->func == NULL)
                        goto restore;
@@ -441,10 +455,11 @@ func_reload(struct func *func)
        struct func_name name;
        func_split_name(func->def->name, &name);
        struct module *module = NULL;
-       if (module_reload(name.package, name.package_end, &module) != 0) {
+       if (module_reload(name.package, name.package_end, &module, false) != 0) 
{
                diag_log();
                return -1;
        }
+
        return 0;
 }
 
diff --git a/src/box/func.h b/src/box/func.h
index 0957546..168c2fc 100644
--- a/src/box/func.h
+++ b/src/box/func.h
@@ -116,6 +116,21 @@ func_call(struct func *func, box_function_ctx_t *ctx, 
const char *args,
 int
 func_reload(struct func *func);
 
+/**
+ * Reload dynamically loadable module.
+ *
+ * @param package name begin pointer.
+ * @param package_end name end pointer.
+ * @param[out] module a pointer to store module object on success.
+ * @param check_access do ACL checks if specified.
+ *
+ * @retval -1 on error
+ * @retval 0 on success
+ */
+int
+module_reload(const char *package, const char *package_end,
+             struct module **module, bool check_access);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/test/box/func_reload.result b/test/box/func_reload.result
index 5aeb85c..9c05555 100644
--- a/test/box/func_reload.result
+++ b/test/box/func_reload.result
@@ -54,6 +54,10 @@ fio.symlink(reload1_path, reload_path)
 box.schema.func.reload("reload.foo")
 ---
 ...
+box.schema.func.reload("reload")
+---
+- error: Function 'reload' does not exist
+...
 -- test of usual case reload. No hanging calls
 box.space.test:insert{0}
 ---
@@ -77,6 +81,9 @@ fio.symlink(reload2_path, reload_path)
 box.schema.func.reload("reload.foo")
 ---
 ...
+box.schema.func.reload("reload")
+---
+...
 c:call("reload.foo")
 ---
 - []
diff --git a/test/box/func_reload.test.lua b/test/box/func_reload.test.lua
index dc56d84..5569dcd 100644
--- a/test/box/func_reload.test.lua
+++ b/test/box/func_reload.test.lua
@@ -21,6 +21,7 @@ fio.symlink(reload1_path, reload_path)
 
 --check not fail on non-load func
 box.schema.func.reload("reload.foo")
+box.schema.func.reload("reload")
 
 -- test of usual case reload. No hanging calls
 box.space.test:insert{0}
@@ -30,6 +31,7 @@ _ = fio.unlink(reload_path)
 fio.symlink(reload2_path, reload_path)
 
 box.schema.func.reload("reload.foo")
+box.schema.func.reload("reload")
 c:call("reload.foo")
 box.space.test:select{}
 box.space.test:truncate()
-- 
2.7.4


Other related posts: