<HTML><BODY>Hi,<br>overall patch looks good with minor fixes (short summary, see explanation below):<br>- fix commit message<br>- change function name<br>- remove unused header<br><br><blockquote style="border-left:1px solid #0857A6; margin:10px; padding:0 0 0 10px;">
        Четверг, 14 июня 2018, 18:04 +03:00 от Ilya Markov <imarkov@tarantool.org>:<br><br><div id=""><div class="js-helper js-readmsg-msg"><div><div id="style_15289886910000000571_BODY">Add required for trigger on_ctl_event<br>
structures and lua bindings.<br><br>
Prerequisite #3259</div></div></div></div></blockquote>#3159<br><blockquote style="border-left:1px solid #0857A6; margin:10px; padding:0 0 0 10px;"><div id=""><div class="js-helper js-readmsg-msg"><div><div id="style_15289886910000000571_BODY"><br>
---<br>
 src/box/CMakeLists.txt   |  1 +<br>
 src/box/box.cc           |  9 ++++++<br>
 src/box/box.h            |  1 +<br>
 src/box/ctl.c            | 63 +++++++++++++++++++++++++++++++++++++<br>
 src/box/ctl.h            | 81 ++++++++++++++++++++++++++++++++++++++++++++++++<br>
 src/box/lua/cfg.cc       | 12 +++++++<br>
 src/box/lua/ctl.c        | 51 ++++++++++++++++++++++++++++++<br>
 src/box/lua/ctl.h        |  2 ++<br>
 src/box/lua/load_cfg.lua | 10 +++++-<br>
 src/cfg.c                | 37 ++++++++++++++++++++++<br>
 src/cfg.h                |  4 +++<br>
 11 files changed, 270 insertions(+), 1 deletion(-)<br>
 create mode 100644 src/box/ctl.c<br>
 create mode 100644 src/box/ctl.h<br><br>
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt<br>
index 6b1ae3e..74f6b54 100644<br>
--- a/src/box/CMakeLists.txt<br>
+++ b/src/box/CMakeLists.txt<br>
@@ -112,6 +112,7 @@ add_library(box STATIC<br>
     journal.c<br>
     wal.c<br>
     call.c<br>
+    ctl.c<br>
     ${lua_sources}<br>
     lua/init.c<br>
     lua/call.c<br>
diff --git a/src/box/box.cc b/src/box/box.cc<br>
index 61bfa11..26277e7 100644<br>
--- a/src/box/box.cc<br>
+++ b/src/box/box.cc<br>
@@ -72,6 +72,7 @@<br>
 #include "call.h"<br>
 #include "func.h"<br>
 #include "sequence.h"<br>
+#include "ctl.h"<br><br>
 static char status[64] = "unknown";<br><br>
@@ -823,6 +824,13 @@ box_set_net_msg_max(void)<br>
                           IPROTO_FIBER_POOL_SIZE_FACTOR);<br>
 }<br><br>
+void<br>
+box_set_on_ctl_event(void)<br>
+{<br>
+       if (cfg_reset_on_ctl_event() < 0)<br>
+               diag_raise();<br>
+}</div></div></div></div></blockquote>Please add _xc suffix, since function raise exception.<br><blockquote style="border-left:1px solid #0857A6; margin:10px; padding:0 0 0 10px;"><div id=""><div class="js-helper js-readmsg-msg"><div><div id="style_15289886910000000571_BODY"><br>
+<br>
 /* }}} configuration bindings */<br><br>
 /**<br>
@@ -1808,6 +1816,7 @@ box_cfg_xc(void)<br>
   box_set_replication_connect_timeout();<br>
   box_set_replication_connect_quorum();<br>
   box_set_replication_skip_conflict();<br>
+       box_set_on_ctl_event();<br>
   replication_sync_lag = box_check_replication_sync_lag();<br>
   xstream_create(&join_stream, apply_initial_join_row);<br>
   xstream_create(&subscribe_stream, apply_row);<br>
diff --git a/src/box/box.h b/src/box/box.h<br>
index d396789..0325527 100644<br>
--- a/src/box/box.h<br>
+++ b/src/box/box.h<br>
@@ -191,6 +191,7 @@ void box_set_replication_connect_timeout(void);<br>
 void box_set_replication_connect_quorum(void);<br>
 void box_set_replication_skip_conflict(void);<br>
 void box_set_net_msg_max(void);<br>
+void box_set_on_ctl_event(void);<br><br>
 extern "C" {<br>
 #endif /* defined(__cplusplus) */<br>
diff --git a/src/box/ctl.c b/src/box/ctl.c<br>
new file mode 100644<br>
index 0000000..fc3ae37<br>
--- /dev/null<br>
+++ b/src/box/ctl.c<br>
@@ -0,0 +1,63 @@<br>
+/*<br>
+ * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file.<br>
+ *<br>
+ * Redistribution and use in source and binary forms, with or<br>
+ * without modification, are permitted provided that the following<br>
+ * conditions are met:<br>
+ *<br>
+ * 1. Redistributions of source code must retain the above<br>
+ *    copyright notice, this list of conditions and the<br>
+ *    following disclaimer.<br>
+ *<br>
+ * 2. Redistributions in binary form must reproduce the above<br>
+ *    copyright notice, this list of conditions and the following<br>
+ *    disclaimer in the documentation and/or other materials<br>
+ *    provided with the distribution.<br>
+ *<br>
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND<br>
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED<br>
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR<br>
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL<br>
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,<br>
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL<br>
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR<br>
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF<br>
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT<br>
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF<br>
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF<br>
+ * SUCH DAMAGE.<br>
+ */<br>
+#include <lib/small/small/rlist.h><br>
+#include <trigger.h><br>
+#include <box/ctl.h><br>
+#include "errcode.h"<br>
+#include "error.h"<br>
+#include <exception.h><br>
+<br>
+RLIST_HEAD(on_ctl_event);<br>
+<br>
+int<br>
+run_on_ctl_event_triggers(const struct on_ctl_event_ctx *result) {<br>
+       return trigger_run(&on_ctl_event, (void *) result);<br>
+}<br>
+<br>
+int<br>
+run_on_ctl_event_trigger_type(enum ctl_event_type type)<br>
+{<br>
+       struct on_ctl_event_ctx ctx = {};<br>
+       ctx.type = type;<br>
+       return run_on_ctl_event_triggers(&ctx);<br>
+}<br>
+<br>
+int<br>
+cfg_reset_on_ctl_event()<br>
+{<br>
+       if (cfg_reset_trigger("on_ctl_event", &on_ctl_event,<br>
+               lbox_push_on_ctl_event, NULL) < 0) {<br>
+               diag_set(ClientError, ER_CFG, "on_ctl_event",<br>
+                        "expected function or table");<br>
+               return -1;<br>
+       }<br>
+       return 0;<br>
+}<br>
\ No newline at end of file<br>
diff --git a/src/box/ctl.h b/src/box/ctl.h<br>
new file mode 100644<br>
index 0000000..1dbb6be<br>
--- /dev/null<br>
+++ b/src/box/ctl.h<br>
@@ -0,0 +1,81 @@<br>
+#ifndef INCLUDES_TARANTOOL_CTL_H<br>
+#define INCLUDES_TARANTOOL_CTL_H<br>
+<br>
+/*<br>
+ * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file.<br>
+ *<br>
+ * Redistribution and use in source and binary forms, with or<br>
+ * without modification, are permitted provided that the following<br>
+ * conditions are met:<br>
+ *<br>
+ * 1. Redistributions of source code must retain the above<br>
+ *    copyright notice, this list of conditions and the<br>
+ *    following disclaimer.<br>
+ *<br>
+ * 2. Redistributions in binary form must reproduce the above<br>
+ *    copyright notice, this list of conditions and the following<br>
+ *    disclaimer in the documentation and/or other materials<br>
+ *    provided with the distribution.<br>
+ *<br>
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND<br>
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED<br>
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR<br>
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL<br>
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,<br>
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL<br>
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR<br>
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF<br>
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT<br>
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF<br>
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF<br>
+ * SUCH DAMAGE.<br>
+ */<br>
+<br>
+#include <cfg.h><br>
+#include <box/lua/ctl.h></div></div></div></div></blockquote><br><blockquote style="border-left:1px solid #0857A6; margin:10px; padding:0 0 0 10px;"><div id=""><div class="js-helper js-readmsg-msg"><div><div id="style_15289886910000000571_BODY"><br>
+#include "errcode.h"<br>
+#include "error.h"<br>
+#include <exception.h></div></div></div></div></blockquote><br>I think last three headers are useless here.<br><blockquote style="border-left:1px solid #0857A6; margin:10px; padding:0 0 0 10px;"><div id=""><div class="js-helper js-readmsg-msg"><div><div id="style_15289886910000000571_BODY"><br>
+<br>
+/** Global on-ctl_event triggers. */<br>
+extern struct rlist on_ctl_event;<br>
+<br>
+enum ctl_event_type {<br>
+       CTL_EVENT_SYSTEM_SPACE_RECOVERY,<br>
+       CTL_EVENT_LOCAL_RECOVERY,<br>
+       CTL_EVENT_READ_ONLY,<br>
+       CTL_EVENT_READ_WRITE,<br>
+       CTL_EVENT_SHUTDOWN,<br>
+       CTL_EVENT_REPLICASET_ADD,<br>
+       CTL_EVENT_REPLICASET_REMOVE,<br>
+};<br>
+<br>
+struct on_ctl_event_ctx {<br>
+       enum ctl_event_type type;<br>
+       uint32_t replica_id;<br>
+};<br>
+<br>
+#if defined(__cplusplus)<br>
+extern "C" {<br>
+#endif /* defined(__cplusplus) */<br>
+<br>
+/**<br>
+ * Runs on_ctl_event triggers with specified context.<br>
+ */<br>
+int<br>
+run_on_ctl_event_triggers(const struct on_ctl_event_ctx *result);<br>
+<br>
+/**<br>
+ * Runs on_ctl_event triggers with specified type.<br>
+ */<br>
+int<br>
+run_on_ctl_event_trigger_type(enum ctl_event_type type);<br>
+<br>
+int<br>
+cfg_reset_on_ctl_event();<br>
+#if defined(__cplusplus)<br>
+} /* extern "C" */<br>
+#endif /* defined(__cplusplus) */<br>
+<br>
+#endif /* INCLUDES_TARANTOOL_LUA_CTL_H */<br>
diff --git a/src/box/lua/cfg.cc b/src/box/lua/cfg.cc<br>
index 5afebc9..c92478f 100644<br>
--- a/src/box/lua/cfg.cc<br>
+++ b/src/box/lua/cfg.cc<br>
@@ -254,6 +254,17 @@ lbox_cfg_set_net_msg_max(struct lua_State *L)<br>
 }<br><br>
 static int<br>
+lbox_cfg_set_on_ctl_event(struct lua_State *L)<br>
+{<br>
+       try {<br>
+               box_set_on_ctl_event();<br>
+       } catch (Exception *) {<br>
+               luaT_error(L);<br>
+       }<br>
+       return 0;<br>
+}<br>
+<br>
+static int<br>
 lbox_cfg_set_worker_pool_threads(struct lua_State *L)<br>
 {<br>
   (void) L;<br>
@@ -331,6 +342,7 @@ box_lua_cfg_init(struct lua_State *L)<br>
           {"cfg_set_replication_skip_conflict", lbox_cfg_set_replication_skip_conflict},<br>
           {"cfg_set_replication_connect_timeout", lbox_cfg_set_replication_connect_timeout},<br>
           {"cfg_set_net_msg_max", lbox_cfg_set_net_msg_max},<br>
+               {"cfg_set_on_ctl_event", lbox_cfg_set_on_ctl_event},<br>
           {NULL, NULL}<br>
   };<br><br>
diff --git a/src/box/lua/ctl.c b/src/box/lua/ctl.c<br>
index 9a105ed..52f320a 100644<br>
--- a/src/box/lua/ctl.c<br>
+++ b/src/box/lua/ctl.c<br>
@@ -35,6 +35,8 @@<br>
 #include <lua.h><br>
 #include <lauxlib.h><br>
 #include <lualib.h><br>
+#include <lua/trigger.h><br>
+#include <box/ctl.h><br><br>
 #include "lua/utils.h"<br><br>
@@ -64,9 +66,37 @@ lbox_ctl_wait_rw(struct lua_State *L)<br>
   return 0;<br>
 }<br><br>
+<br>
+int<br>
+lbox_push_on_ctl_event(struct lua_State *L, void *event)<br>
+{<br>
+       struct on_ctl_event_ctx *ctx = (struct on_ctl_event_ctx *) event;<br>
+       lua_newtable(L);<br>
+       lua_pushstring(L, "type");<br>
+       lua_pushinteger(L, ctx->type);<br>
+       lua_settable(L, -3);<br>
+<br>
+       if (ctx->type == CTL_EVENT_REPLICASET_ADD ||<br>
+               ctx->type == CTL_EVENT_REPLICASET_REMOVE) {<br>
+               lua_pushstring(L, "replica_id");<br>
+               luaL_pushuint64(L, ctx->replica_id);<br>
+               lua_settable(L, -3);<br>
+       }<br>
+       return 1;<br>
+}<br>
+<br>
+static int<br>
+lbox_on_ctl_event(struct lua_State *L)<br>
+{<br>
+       return lbox_trigger_reset(L, 2, &on_ctl_event,<br>
+                                 lbox_push_on_ctl_event, NULL);<br>
+}<br>
+<br>
+<br>
 static const struct luaL_Reg lbox_ctl_lib[] = {<br>
   {"wait_ro", lbox_ctl_wait_ro},<br>
   {"wait_rw", lbox_ctl_wait_rw},<br>
+       {"on_ctl_event", lbox_on_ctl_event},<br>
   {NULL, NULL}<br>
 };<br><br>
@@ -75,4 +105,25 @@ box_lua_ctl_init(struct lua_State *L)<br>
 {<br>
   luaL_register_module(L, "box.ctl", lbox_ctl_lib);<br>
   lua_pop(L, 1);<br>
+<br>
+       luaL_findtable(L, LUA_GLOBALSINDEX, "box.ctl", 1);<br>
+       lua_newtable(L);<br>
+       lua_setfield(L, -2, "event");<br>
+       lua_getfield(L, -1, "event");<br>
+<br>
+       lua_pushnumber(L, CTL_EVENT_SYSTEM_SPACE_RECOVERY);<br>
+       lua_setfield(L, -2, "SYSTEM_SPACE_RECOVERY");<br>
+       lua_pushnumber(L, CTL_EVENT_LOCAL_RECOVERY);<br>
+       lua_setfield(L, -2, "LOCAL_RECOVERY");<br>
+       lua_pushnumber(L, CTL_EVENT_READ_ONLY);<br>
+       lua_setfield(L, -2, "READ_ONLY");<br>
+       lua_pushnumber(L, CTL_EVENT_READ_WRITE);<br>
+       lua_setfield(L, -2, "READ_WRITE");<br>
+       lua_pushnumber(L, CTL_EVENT_SHUTDOWN);<br>
+       lua_setfield(L, -2, "SHUTDOWN");<br>
+       lua_pushnumber(L, CTL_EVENT_REPLICASET_ADD);<br>
+       lua_setfield(L, -2, "CTL_EVENT_REPLICASET_ADD");<br>
+       lua_pushnumber(L, CTL_EVENT_REPLICASET_REMOVE);<br>
+       lua_setfield(L, -2, "CTL_EVENT_REPLICASET_REMOVE");<br>
+       lua_pop(L, 2); /* box, ctl */<br>
 }<br>
diff --git a/src/box/lua/ctl.h b/src/box/lua/ctl.h<br>
index e7c2edd..ab63232 100644<br>
--- a/src/box/lua/ctl.h<br>
+++ b/src/box/lua/ctl.h<br>
@@ -41,6 +41,8 @@ struct lua_State;<br>
 void<br>
 box_lua_ctl_init(struct lua_State *L);<br><br>
+int<br>
+lbox_push_on_ctl_event(struct lua_State *L, void *event);<br>
 #if defined(__cplusplus)<br>
 } /* extern "C" */<br>
 #endif /* defined(__cplusplus) */<br>
diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua<br>
index 0b668cd..c4d1347 100644<br>
--- a/src/box/lua/load_cfg.lua<br>
+++ b/src/box/lua/load_cfg.lua<br>
@@ -1,7 +1,11 @@<br>
 -- load_cfg.lua - internal file<br><br>
 local log = require('log')<br>
-local json = require('json')<br>
+local json = require("json").new()<br>
+json.cfg{<br>
+    encode_use_tostring    = true,<br>
+}<br>
+<br>
 local private = require('box.internal')<br>
 local urilib = require('uri')<br>
 local math = require('math')<br>
@@ -64,6 +68,7 @@ local default_cfg = {<br>
     feedback_host         = "<a href="https://feedback.tarantool.io" target="_blank">https://feedback.tarantool.io</a>",<br>
     feedback_interval     = 3600,<br>
     net_msg_max           = 768,<br>
+    on_ctl_event          = nil,<br>
 }<br><br>
 -- types of available options<br>
@@ -125,6 +130,7 @@ local template_cfg = {<br>
     feedback_host         = 'string',<br>
     feedback_interval     = 'number',<br>
     net_msg_max           = 'number',<br>
+    on_ctl_event          = 'function, table',<br>
 }<br><br>
 local function normalize_uri(port)<br>
@@ -216,6 +222,7 @@ local dynamic_cfg = {<br>
     replicaset_uuid         = check_replicaset_uuid,<br>
     replication_skip_conflict = private.cfg_set_replication_skip_conflict,<br>
     net_msg_max             = private.cfg_set_net_msg_max,<br>
+    on_ctl_event            = private.cfg_set_on_ctl_event,<br>
 }<br><br>
 local dynamic_cfg_skip_at_load = {<br>
@@ -232,6 +239,7 @@ local dynamic_cfg_skip_at_load = {<br>
     force_recovery          = true,<br>
     instance_uuid           = true,<br>
     replicaset_uuid         = true,<br>
+    on_ctl_event            = true,<br>
 }<br><br>
 local function convert_gb(size)<br>
diff --git a/src/cfg.c b/src/cfg.c<br>
index 7c7d6e7..b07c1f3 100644<br>
--- a/src/cfg.c<br>
+++ b/src/cfg.c<br>
@@ -153,3 +153,40 @@ cfg_getarr_elem(const char *name, int i)<br>
   lua_pop(tarantool_L, 2);<br>
   return val;<br>
 }<br>
+<br>
+int<br>
+cfg_reset_trigger(const char *name, struct rlist *list,<br>
+                 lbox_push_event_f push_event, lbox_pop_event_f pop_event)<br>
+{<br>
+       cfg_get(name);<br>
+       struct lua_State *L = tarantool_L;<br>
+       if (lua_isnil(L, -1))<br>
+               return 0;<br>
+       if (!lua_isfunction(L, -1) && !lua_istable(L, -1)) {<br>
+               return -1;<br>
+       }<br>
+       if (lua_istable(L, -1)) {<br>
+               lua_pushinteger(L, 1);<br>
+               lua_gettable(L, -2);<br>
+               if (!luaL_isnull(L, lua_gettop(L)) && !lua_isfunction(L, -1))<br>
+                       return -1;<br>
+               bool is_nil = luaL_isnull(L, lua_gettop(L));<br>
+               if (is_nil) {<br>
+                       lua_pop(L, 1);<br>
+                       lua_pushnil(L);<br>
+               }<br>
+               lua_pushinteger(L, 2);<br>
+               lua_gettable(L, -3);<br>
+               if (!lua_isfunction(L, -1) && !lua_isnil(L, -1))<br>
+                       return -1;<br>
+               if (is_nil && lua_isnil(L, -1)) {<br>
+                       return 0;<br>
+               }<br>
+       } else {<br>
+               lua_pushnil(L);<br>
+       }<br>
+       int rc = lbox_trigger_reset(L, lua_gettop(L), list,<br>
+                                 push_event, pop_event);<br>
+       lua_pop(L, 1);<br>
+       return rc;<br>
+}<br>
\ No newline at end of file<br>
diff --git a/src/cfg.h b/src/cfg.h<br>
index <span class="js-phone-number">8499388</span>..d36465c 100644<br>
--- a/src/cfg.h<br>
+++ b/src/cfg.h<br>
@@ -32,6 +32,7 @@<br>
  */<br><br>
 #include <stdint.h><br>
+#include <lua/trigger.h><br><br>
 #if defined(__cplusplus)<br>
 extern "C" {<br>
@@ -61,6 +62,9 @@ cfg_getarr_size(const char *name);<br>
 const char *<br>
 cfg_getarr_elem(const char *name, int i);<br><br>
+int<br>
+cfg_reset_trigger(const char *name, struct rlist *list,<br>
+                 lbox_push_event_f push_event, lbox_pop_event_f pop_event);<br>
 #if defined(__cplusplus)<br>
 } /* extern "C" */<br>
 #endif /* defined(__cplusplus) */<br>
-- <br>
2.7.4<br><br><br></div></div></div></div></blockquote>
<br>
<br>Best regards,<br>Konstantin Belyavskiy<br>k.belyavskiy@tarantool.org<br></BODY></HTML>