3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "common/c_internal.h"
21 #include "util/numeric.h"
26 #include <algorithm> // std::find
28 std::string script_get_backtrace(lua_State *L)
30 lua_getglobal(L, "debug");
31 lua_getfield(L, -1, "traceback");
33 std::string result = luaL_checkstring(L, -1);
38 int script_exception_wrapper(lua_State *L, lua_CFunction f)
41 return f(L); // Call wrapped function and return result.
42 } catch (const char *s) { // Catch and convert exceptions.
44 } catch (std::exception &e) {
45 lua_pushstring(L, e.what());
47 return lua_error(L); // Rethrow as a Lua error.
50 int script_error_handler(lua_State *L)
52 lua_getglobal(L, "core");
53 lua_getfield(L, -1, "error_handler");
54 if (!lua_isnil(L, -1)) {
57 // No Lua error handler available. Call debug.traceback(tostring(#1), level).
58 lua_getglobal(L, "debug");
59 lua_getfield(L, -1, "traceback");
60 lua_getglobal(L, "tostring");
64 lua_pushinteger(L, 2); // Stack level
70 * Note that we can't get tracebacks for LUA_ERRMEM or LUA_ERRERR (without
71 * hacking Lua internals). For LUA_ERRMEM, this is because memory errors will
72 * not execute the error handler, and by the time lua_pcall returns the
73 * execution stack will have already been unwound. For LUA_ERRERR, there was
74 * another error while trying to generate a backtrace from a LUA_ERRRUN. It is
75 * presumed there is an error with the internal Lua state and thus not possible
76 * to gather a coherent backtrace. Realistically, the best we can do here is
77 * print which C function performed the failing pcall.
79 void script_error(lua_State *L, int pcall_result, const char *mod, const char *fxn)
81 if (pcall_result == 0)
85 switch (pcall_result) {
93 err_type = "Double fault";
105 const char *err_descr = lua_tostring(L, -1);
107 err_descr = "<no description>";
110 porting::mt_snprintf(buf, sizeof(buf), "%s error from mod '%s' in callback %s(): ",
113 std::string err_msg(buf);
114 err_msg += err_descr;
116 if (pcall_result == LUA_ERRMEM) {
117 err_msg += "\nCurrent Lua memory usage: "
118 + itos(lua_gc(L, LUA_GCCOUNT, 0) >> 10) + " MB";
121 throw LuaError(err_msg);
124 static void script_log_add_source(lua_State *L, std::string &message, int stack_depth)
128 if (lua_getstack(L, stack_depth, &ar)) {
129 FATAL_ERROR_IF(!lua_getinfo(L, "Sl", &ar), "lua_getinfo() failed");
130 message.append(" (at " + std::string(ar.short_src) + ":"
131 + std::to_string(ar.currentline) + ")");
133 message.append(" (at ?:?)");
137 bool script_log_unique(lua_State *L, std::string message, std::ostream &log_to,
140 thread_local std::vector<u64> logged_messages;
142 script_log_add_source(L, message, stack_depth);
143 u64 hash = murmur_hash_64_ua(message.data(), message.length(), 0xBADBABE);
145 if (std::find(logged_messages.begin(), logged_messages.end(), hash)
146 == logged_messages.end()) {
148 logged_messages.emplace_back(hash);
149 log_to << message << std::endl;
155 DeprecatedHandlingMode get_deprecated_handling_mode()
157 static thread_local bool configured = false;
158 static thread_local DeprecatedHandlingMode ret = DeprecatedHandlingMode::Ignore;
160 // Only read settings on first call
162 std::string value = g_settings->get("deprecated_lua_api_handling");
163 if (value == "log") {
164 ret = DeprecatedHandlingMode::Log;
165 } else if (value == "error") {
166 ret = DeprecatedHandlingMode::Error;
174 void log_deprecated(lua_State *L, std::string message, int stack_depth)
176 DeprecatedHandlingMode mode = get_deprecated_handling_mode();
177 if (mode == DeprecatedHandlingMode::Ignore)
180 script_log_add_source(L, message, stack_depth);
181 warningstream << message << std::endl;
183 if (mode == DeprecatedHandlingMode::Error)
184 script_error(L, LUA_ERRRUN, NULL, NULL);
186 infostream << script_get_backtrace(L) << std::endl;
189 void call_string_dump(lua_State *L, int idx)
191 // Retrieve string.dump from insecure env to avoid it being tampered with
192 lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
193 if (!lua_isnil(L, -1))
194 lua_getfield(L, -1, "string");
196 lua_getglobal(L, "string");
197 lua_getfield(L, -1, "dump");
198 lua_remove(L, -2); // remove _G
199 lua_remove(L, -2); // remove 'string' table
200 lua_pushvalue(L, idx);