Browse Source

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

bel2125 4 năm trước cách đây
mục cha
commit
cb537c40f6
4 tập tin đã thay đổi với 130 bổ sung38 xóa
  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.
 In the future, some callback functions will be available to notify the
 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.
-Parameters mapped to global 'mg' table 'params' field.
+Parameters mapped into 'mg.params' as table.
+Example: `paramName1=paramValue1,paramName2=2`
 
 ### lua\_preload\_file
 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.
 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".
 
 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
 [websocket.lua](https://github.com/civetweb/civetweb/blob/master/test/websocket.lua).
 
+##Lua background script
+
 
 # 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)
 static int MakeConsole(void);
 #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)
 {
 	struct stat st;
@@ -1018,12 +1037,14 @@ verify_existence(char **options, const char *option_name, int must_be_dir)
 	if (path != NULL
 	    && (stat(path, &st) != 0
 	        || ((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;
 }
 
-static void
+
+static int
 sanitize_options(char *options[] /* server options */,
                  const char *arg0 /* argv[0] */)
 {
+	int ok = 1;
 	/* Make sure we have absolute paths for files and directories */
 	set_absolute_path(options, "document_root", 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);
 
 	/* 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)
-	verify_existence(options, "lua_preload_file", 0);
+	if (!verify_existence(options, "lua_preload_file", 0))
+		ok = 0;
 #endif
+	return ok;
 }
 
 
@@ -1384,7 +1414,10 @@ start_civetweb(int argc, char *argv[])
 	/* Update config based on command line arguments */
 	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 */
 	signal(SIGTERM, signal_handler);
@@ -1435,6 +1468,7 @@ start_civetweb(int argc, char *argv[])
 		    g_server_name,
 		    ((g_user_data.first_message == NULL) ? "unknown reason"
 		                                         : g_user_data.first_message));
+		/* TODO: Edit file g_config_file_name */
 	}
 
 #if defined(MG_EXPERIMENTAL_INTERFACES)
@@ -1450,7 +1484,10 @@ start_civetweb(int argc, char *argv[])
 			    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);
 		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_environment_type = 4;
 static const char lua_regkey_dtor = 5;
-static const char *const LUABACKGROUNDPARAMS = "mg";
 
 
 /* Limit nesting depth of mg.include.
@@ -2172,8 +2171,8 @@ lwebsocket_set_timer(lua_State *L, int is_periodic)
 
 		/* Argument for timer */
 		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 + 7, action_txt, action_txt_len);
 		arg->txt[action_txt_len + 7] = ')';
@@ -2207,8 +2206,8 @@ lwebsocket_set_timer(lua_State *L, int is_periodic)
 
 		/* Argument for timer */
 		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;
 		if (0
 		    == timer_add(ctx,
@@ -2414,9 +2413,10 @@ lua_debug_hook(lua_State *L, lua_Debug *ar)
 
 /* Lua Environment */
 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:
 		reg_string(L, "lua_type", "websocket");
 		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, "read", lsp_read, 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) {
 		reg_function(L, "write", lwebsock_write);
+	}
+
 #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_interval", lwebsocket_set_interval);
 #endif
@@ -3323,7 +3330,7 @@ lua_websocket_close(struct mg_connection *conn, void *ws_arg)
 
 
 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,
                               char *ebuf,
                               size_t ebuf_len)
@@ -3332,8 +3339,6 @@ mg_prepare_lua_context_script(const char *file_name,
 	int lua_ret;
 	const char *lua_err_txt;
 
-	(void)ctx;
-
 	L = luaL_newstate();
 	if (L == NULL) {
 		mg_snprintf(NULL,
@@ -3344,8 +3349,16 @@ mg_prepare_lua_context_script(const char *file_name,
 		            "Cannot create Lua state");
 		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);
 	if (lua_ret != LUA_OK) {
 		/* 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);
 		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 */
 	lua_ret = lua_pcall(L,
 	                    /* no arguments */ 0,
 	                    /* zero or one return value */ 1,
-	                    /* errors as strint return value */ 0);
+	                    /* errors as string return value */ 0);
 
 	if (lua_ret != LUA_OK) {
 		/* Error when executing the script */
@@ -3383,6 +3412,17 @@ mg_prepare_lua_context_script(const char *file_name,
 		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 */
 	return L;
 }
@@ -3420,13 +3460,19 @@ lua_ctx_exit(struct mg_context *ctx)
 }
 
 
+/* Execute Lua script from main */
 int
 run_lua(const char *file_name)
 {
 	int func_ret = EXIT_FAILURE;
 	char ebuf[512] = {0};
 	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) {
 		/* Script executed */
 		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;