Browse Source

Allow Lua background scripts to use timers (Step 1/?)

bel2125 4 years ago
parent
commit
cb537c40f6
4 changed files with 130 additions and 38 deletions
  1. 7 7
      docs/UserManual.md
  2. 52 15
      src/main.c
  3. 62 16
      src/mod_lua.inl
  4. 9 0
      test/lua_backbround_script_timer.lua

+ 7 - 7
docs/UserManual.md

@@ -448,14 +448,12 @@ files, ...), check for external resources, remove old log files, etc.
 The Lua state remains open until the server is stopped.
 The Lua state remains open until the server is stopped.
 In the future, some callback functions will be available to notify the
 In the future, some callback functions will be available to notify the
 script on changes of the server state. See example lua script :
 script on changes of the server state. See example lua script :
-[background.lua](https://github.com/civetweb/civetweb/blob/master/test/background.lua).
+[background.lua](https://github.com/civetweb/civetweb/blob/master/test/lua_backbround_script_timer.lua).
 
 
-Additional functions available in background script :
-sleep, root path, script name, is terminated
-
-### lua\_background\_script\_params `param1=1,param2=2`
+### lua\_background\_script\_params
 Can add dynamic parameters to background script.
 Can add dynamic parameters to background script.
-Parameters mapped to global 'mg' table 'params' field.
+Parameters mapped into 'mg.params' as table.
+Example: `paramName1=paramValue1,paramName2=2`
 
 
 ### lua\_preload\_file
 ### lua\_preload\_file
 This configuration option can be used to specify a Lua script file, which
 This configuration option can be used to specify a Lua script file, which
@@ -953,7 +951,7 @@ or using Lua code:
 
 
 or Lua Server Pages generating HTML content MAY skip the HTTP header lines.
 or Lua Server Pages generating HTML content MAY skip the HTTP header lines.
 In this case, CivetWeb automatically creates a "200 OK"/"Content-Type: text/html"
 In this case, CivetWeb automatically creates a "200 OK"/"Content-Type: text/html"
-reply header. In this case, the document should start with "<!DOCTYPE html>"
+reply header. In this case, the document must start with "<!DOCTYPE html>"
 or "<html".
 or "<html".
 
 
 Currently the extended "Kepler Syntax" is available only for text/html pages
 Currently the extended "Kepler Syntax" is available only for text/html pages
@@ -982,6 +980,8 @@ Lua websocket pages do support single shot (timeout) and interval timers.
 An example is shown in
 An example is shown in
 [websocket.lua](https://github.com/civetweb/civetweb/blob/master/test/websocket.lua).
 [websocket.lua](https://github.com/civetweb/civetweb/blob/master/test/websocket.lua).
 
 
+##Lua background script
+
 
 
 # Using CGI
 # Using CGI
 
 

+ 52 - 15
src/main.c

@@ -268,6 +268,25 @@ die(const char *fmt, ...)
 }
 }
 
 
 
 
+static void
+warn(const char *fmt, ...)
+{
+	va_list ap;
+	char msg[512] = "";
+
+	va_start(ap, fmt);
+	(void)vsnprintf(msg, sizeof(msg) - 1, fmt, ap);
+	msg[sizeof(msg) - 1] = 0;
+	va_end(ap);
+
+#if defined(_WIN32)
+	MessageBox(NULL, msg, "Warning", MB_OK);
+#else
+	fprintf(stderr, "%s\n", msg);
+#endif
+}
+
+
 #if defined(WIN32)
 #if defined(WIN32)
 static int MakeConsole(void);
 static int MakeConsole(void);
 #endif
 #endif
@@ -993,7 +1012,7 @@ is_path_absolute(const char *path)
 }
 }
 
 
 
 
-static void
+static int
 verify_existence(char **options, const char *option_name, int must_be_dir)
 verify_existence(char **options, const char *option_name, int must_be_dir)
 {
 {
 	struct stat st;
 	struct stat st;
@@ -1018,12 +1037,14 @@ verify_existence(char **options, const char *option_name, int must_be_dir)
 	if (path != NULL
 	if (path != NULL
 	    && (stat(path, &st) != 0
 	    && (stat(path, &st) != 0
 	        || ((S_ISDIR(st.st_mode) ? 1 : 0) != must_be_dir))) {
 	        || ((S_ISDIR(st.st_mode) ? 1 : 0) != must_be_dir))) {
-		die("Invalid path for %s: [%s]: (%s). Make sure that path is either "
-		    "absolute, or it is relative to civetweb executable.",
-		    option_name,
-		    path,
-		    strerror(errno));
+		warn("Invalid path for %s: [%s]: (%s). Make sure that path is either "
+		     "absolute, or it is relative to civetweb executable.",
+		     option_name,
+		     path,
+		     strerror(errno));
+		return 0;
 	}
 	}
+	return 1;
 }
 }
 
 
 
 
@@ -1251,10 +1272,12 @@ run_client(const char *url_arg)
 	return 1;
 	return 1;
 }
 }
 
 
-static void
+
+static int
 sanitize_options(char *options[] /* server options */,
 sanitize_options(char *options[] /* server options */,
                  const char *arg0 /* argv[0] */)
                  const char *arg0 /* argv[0] */)
 {
 {
+	int ok = 1;
 	/* Make sure we have absolute paths for files and directories */
 	/* Make sure we have absolute paths for files and directories */
 	set_absolute_path(options, "document_root", arg0);
 	set_absolute_path(options, "document_root", arg0);
 	set_absolute_path(options, "put_delete_auth_file", arg0);
 	set_absolute_path(options, "put_delete_auth_file", arg0);
@@ -1268,14 +1291,21 @@ sanitize_options(char *options[] /* server options */,
 	set_absolute_path(options, "ssl_certificate", arg0);
 	set_absolute_path(options, "ssl_certificate", arg0);
 
 
 	/* Make extra verification for certain options */
 	/* Make extra verification for certain options */
-	verify_existence(options, "document_root", 1);
-	verify_existence(options, "cgi_interpreter", 0);
-	verify_existence(options, "ssl_certificate", 0);
-	verify_existence(options, "ssl_ca_path", 1);
-	verify_existence(options, "ssl_ca_file", 0);
+	if (!verify_existence(options, "document_root", 1))
+		ok = 0;
+	if (!verify_existence(options, "cgi_interpreter", 0))
+		ok = 0;
+	if (!verify_existence(options, "ssl_certificate", 0))
+		ok = 0;
+	if (!verify_existence(options, "ssl_ca_path", 1))
+		ok = 0;
+	if (!verify_existence(options, "ssl_ca_file", 0))
+		ok = 0;
 #if defined(USE_LUA)
 #if defined(USE_LUA)
-	verify_existence(options, "lua_preload_file", 0);
+	if (!verify_existence(options, "lua_preload_file", 0))
+		ok = 0;
 #endif
 #endif
+	return ok;
 }
 }
 
 
 
 
@@ -1384,7 +1414,10 @@ start_civetweb(int argc, char *argv[])
 	/* Update config based on command line arguments */
 	/* Update config based on command line arguments */
 	process_command_line_arguments(argc, argv, options);
 	process_command_line_arguments(argc, argv, options);
 
 
-	sanitize_options(options, argv[0]);
+	i = sanitize_options(options, argv[0]);
+	if (!i) {
+		die("Invalid options");
+	}
 
 
 	/* Setup signal handler: quit on Ctrl-C */
 	/* Setup signal handler: quit on Ctrl-C */
 	signal(SIGTERM, signal_handler);
 	signal(SIGTERM, signal_handler);
@@ -1435,6 +1468,7 @@ start_civetweb(int argc, char *argv[])
 		    g_server_name,
 		    g_server_name,
 		    ((g_user_data.first_message == NULL) ? "unknown reason"
 		    ((g_user_data.first_message == NULL) ? "unknown reason"
 		                                         : g_user_data.first_message));
 		                                         : g_user_data.first_message));
+		/* TODO: Edit file g_config_file_name */
 	}
 	}
 
 
 #if defined(MG_EXPERIMENTAL_INTERFACES)
 #if defined(MG_EXPERIMENTAL_INTERFACES)
@@ -1450,7 +1484,10 @@ start_civetweb(int argc, char *argv[])
 			    strerror(errno));
 			    strerror(errno));
 		}
 		}
 
 
-		sanitize_options(options, argv[0]);
+		j = sanitize_options(options, argv[0]);
+		if (!j) {
+			die("Invalid options");
+		}
 
 
 		j = mg_start_domain(g_ctx, (const char **)options);
 		j = mg_start_domain(g_ctx, (const char **)options);
 		if (j < 0) {
 		if (j < 0) {

+ 62 - 16
src/mod_lua.inl

@@ -57,7 +57,6 @@ static const char lua_regkey_connlist = 2;
 static const char lua_regkey_lsp_include_history = 3;
 static const char lua_regkey_lsp_include_history = 3;
 static const char lua_regkey_environment_type = 4;
 static const char lua_regkey_environment_type = 4;
 static const char lua_regkey_dtor = 5;
 static const char lua_regkey_dtor = 5;
-static const char *const LUABACKGROUNDPARAMS = "mg";
 
 
 
 
 /* Limit nesting depth of mg.include.
 /* Limit nesting depth of mg.include.
@@ -2172,8 +2171,8 @@ lwebsocket_set_timer(lua_State *L, int is_periodic)
 
 
 		/* Argument for timer */
 		/* Argument for timer */
 		arg->L = L;
 		arg->L = L;
-		arg->script = ws->script;
-		arg->pmutex = &(ws->ws_mutex);
+		arg->script = (ws ? ws->script : NULL);
+		arg->pmutex = (ws ? &(ws->ws_mutex) : NULL);
 		memcpy(arg->txt, "return(", 7);
 		memcpy(arg->txt, "return(", 7);
 		memcpy(arg->txt + 7, action_txt, action_txt_len);
 		memcpy(arg->txt + 7, action_txt, action_txt_len);
 		arg->txt[action_txt_len + 7] = ')';
 		arg->txt[action_txt_len + 7] = ')';
@@ -2207,8 +2206,8 @@ lwebsocket_set_timer(lua_State *L, int is_periodic)
 
 
 		/* Argument for timer */
 		/* Argument for timer */
 		arg->L = L;
 		arg->L = L;
-		arg->script = ws->script;
-		arg->pmutex = &(ws->ws_mutex);
+		arg->script = (ws ? ws->script : NULL);
+		arg->pmutex = (ws ? &(ws->ws_mutex) : NULL);
 		arg->funcref = funcref;
 		arg->funcref = funcref;
 		if (0
 		if (0
 		    == timer_add(ctx,
 		    == timer_add(ctx,
@@ -2414,9 +2413,10 @@ lua_debug_hook(lua_State *L, lua_Debug *ar)
 
 
 /* Lua Environment */
 /* Lua Environment */
 enum {
 enum {
-	LUA_ENV_TYPE_LUA_SERVER_PAGE = 0,
-	LUA_ENV_TYPE_PLAIN_LUA_PAGE = 1,
-	LUA_ENV_TYPE_LUA_WEBSOCKET = 2,
+	LUA_ENV_TYPE_LUA_SERVER_PAGE = 0, /* page.lp */
+	LUA_ENV_TYPE_PLAIN_LUA_PAGE = 1,  /* script.lua */
+	LUA_ENV_TYPE_LUA_WEBSOCKET = 2,   /* websock.lua */
+	LUA_ENV_TYPE_BACKGROUND = 9 /* Lua backgrond script or exec from cmdline */
 };
 };
 
 
 
 
@@ -2708,10 +2708,13 @@ prepare_lua_environment(struct mg_context *ctx,
 	case LUA_ENV_TYPE_LUA_WEBSOCKET:
 	case LUA_ENV_TYPE_LUA_WEBSOCKET:
 		reg_string(L, "lua_type", "websocket");
 		reg_string(L, "lua_type", "websocket");
 		break;
 		break;
+	case LUA_ENV_TYPE_BACKGROUND:
+		reg_string(L, "lua_type", "background");
+		break;
 	}
 	}
 
 
-	if (lua_env_type == LUA_ENV_TYPE_LUA_SERVER_PAGE
-	    || lua_env_type == LUA_ENV_TYPE_PLAIN_LUA_PAGE) {
+	if ((lua_env_type == LUA_ENV_TYPE_LUA_SERVER_PAGE)
+	    || (lua_env_type == LUA_ENV_TYPE_PLAIN_LUA_PAGE)) {
 		reg_conn_function(L, "cry", lsp_cry, conn);
 		reg_conn_function(L, "cry", lsp_cry, conn);
 		reg_conn_function(L, "read", lsp_read, conn);
 		reg_conn_function(L, "read", lsp_read, conn);
 		reg_conn_function(L, "write", lsp_write, conn);
 		reg_conn_function(L, "write", lsp_write, conn);
@@ -2729,7 +2732,11 @@ prepare_lua_environment(struct mg_context *ctx,
 
 
 	if (lua_env_type == LUA_ENV_TYPE_LUA_WEBSOCKET) {
 	if (lua_env_type == LUA_ENV_TYPE_LUA_WEBSOCKET) {
 		reg_function(L, "write", lwebsock_write);
 		reg_function(L, "write", lwebsock_write);
+	}
+
 #if defined(USE_TIMERS)
 #if defined(USE_TIMERS)
+	if ((lua_env_type == LUA_ENV_TYPE_LUA_WEBSOCKET)
+	    || (lua_env_type == LUA_ENV_TYPE_BACKGROUND)) {
 		reg_function(L, "set_timeout", lwebsocket_set_timeout);
 		reg_function(L, "set_timeout", lwebsocket_set_timeout);
 		reg_function(L, "set_interval", lwebsocket_set_interval);
 		reg_function(L, "set_interval", lwebsocket_set_interval);
 #endif
 #endif
@@ -3323,7 +3330,7 @@ lua_websocket_close(struct mg_connection *conn, void *ws_arg)
 
 
 
 
 static lua_State *
 static lua_State *
-mg_prepare_lua_context_script(const char *file_name,
+mg_lua_context_script_prepare(const char *file_name,
                               struct mg_context *ctx,
                               struct mg_context *ctx,
                               char *ebuf,
                               char *ebuf,
                               size_t ebuf_len)
                               size_t ebuf_len)
@@ -3332,8 +3339,6 @@ mg_prepare_lua_context_script(const char *file_name,
 	int lua_ret;
 	int lua_ret;
 	const char *lua_err_txt;
 	const char *lua_err_txt;
 
 
-	(void)ctx;
-
 	L = luaL_newstate();
 	L = luaL_newstate();
 	if (L == NULL) {
 	if (L == NULL) {
 		mg_snprintf(NULL,
 		mg_snprintf(NULL,
@@ -3344,8 +3349,16 @@ mg_prepare_lua_context_script(const char *file_name,
 		            "Cannot create Lua state");
 		            "Cannot create Lua state");
 		return 0;
 		return 0;
 	}
 	}
-	civetweb_open_lua_libs(L);
 
 
+	/* Add all libraries */
+	prepare_lua_environment(ctx,
+	                        NULL /* conn */,
+	                        NULL /* WS list*/,
+	                        L,
+	                        file_name,
+	                        LUA_ENV_TYPE_BACKGROUND);
+
+	/* Load lua script file */
 	lua_ret = luaL_loadfile(L, file_name);
 	lua_ret = luaL_loadfile(L, file_name);
 	if (lua_ret != LUA_OK) {
 	if (lua_ret != LUA_OK) {
 		/* Error when loading the file (e.g. file not found,
 		/* Error when loading the file (e.g. file not found,
@@ -3362,12 +3375,28 @@ mg_prepare_lua_context_script(const char *file_name,
 		lua_close(L);
 		lua_close(L);
 		return 0;
 		return 0;
 	}
 	}
+	/*	lua_close(L); must be done somewhere else */
+	return L;
+}
+
+
+static lua_State *
+mg_lua_context_script_run(lua_State *L,
+                          const char *file_name,
+                          struct mg_context *ctx,
+                          char *ebuf,
+                          size_t ebuf_len)
+{
+	int lua_ret;
+	const char *lua_err_txt;
+
+	(void)ctx;
 
 
 	/* The script file is loaded, now call it */
 	/* The script file is loaded, now call it */
 	lua_ret = lua_pcall(L,
 	lua_ret = lua_pcall(L,
 	                    /* no arguments */ 0,
 	                    /* no arguments */ 0,
 	                    /* zero or one return value */ 1,
 	                    /* zero or one return value */ 1,
-	                    /* errors as strint return value */ 0);
+	                    /* errors as string return value */ 0);
 
 
 	if (lua_ret != LUA_OK) {
 	if (lua_ret != LUA_OK) {
 		/* Error when executing the script */
 		/* Error when executing the script */
@@ -3383,6 +3412,17 @@ mg_prepare_lua_context_script(const char *file_name,
 		return 0;
 		return 0;
 	}
 	}
 
 
+	/* Check optional return value */
+	if (lua_isboolean(L, -1)) {
+		/* A boolean return value false indicates failure */
+		int ret = lua_toboolean(L, -1);
+		if (ret == 0) {
+			/* Script returned false */
+			lua_close(L);
+			return 0;
+		}
+	}
+
 	/*	lua_close(L); must be done somewhere else */
 	/*	lua_close(L); must be done somewhere else */
 	return L;
 	return L;
 }
 }
@@ -3420,13 +3460,19 @@ lua_ctx_exit(struct mg_context *ctx)
 }
 }
 
 
 
 
+/* Execute Lua script from main */
 int
 int
 run_lua(const char *file_name)
 run_lua(const char *file_name)
 {
 {
 	int func_ret = EXIT_FAILURE;
 	int func_ret = EXIT_FAILURE;
 	char ebuf[512] = {0};
 	char ebuf[512] = {0};
 	lua_State *L =
 	lua_State *L =
-	    mg_prepare_lua_context_script(file_name, NULL, ebuf, sizeof(ebuf));
+	    mg_lua_context_script_prepare(file_name, NULL, ebuf, sizeof(ebuf));
+
+	if (L) {
+		L = mg_lua_context_script_run(L, file_name, NULL, ebuf, sizeof(ebuf));
+	}
+
 	if (L) {
 	if (L) {
 		/* Script executed */
 		/* Script executed */
 		if (lua_type(L, -1) == LUA_TNUMBER) {
 		if (lua_type(L, -1) == LUA_TNUMBER) {

+ 9 - 0
test/lua_backbround_script_timer.lua

@@ -0,0 +1,9 @@
+shared.timer = 0
+
+function timer()
+	shared.timer = shared.timer + 1
+end
+
+mg.set_interval(timer,5)
+
+return true;