xlua: wrap lua_State pointer in struct xlua_state

This will allow us to associate additional fiels with the lua state.
Since the wrapper structure (struct xlua_state) is a different type than
lua_State, the compiler will catch any usage errors - i.e., passing
xlua_state to a function that expects a lua_State and vice versa.

This wrapper nicely delineates C functions that are called by C code and C
functions called (possibly indirectly) by lua.

Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
M hlog/common/config.c +8 -5
@@ 120,6 120,7 @@ static int process_config(lua_State *L, 
 
 int load_config(int data_dir_fd, struct qso *template, const char *mode)
 {
+	struct xlua_state *lua;
 	char error[128];
 	lua_State *L;
 	int ret;

          
@@ 128,13 129,15 @@ int load_config(int data_dir_fd, struct 
 	qso_line_init_defaults();
 
 	/* load the config; it should return a table with desired settings */
-	L = xlua_init_at(data_dir_fd, DATA_DIR_CONFIG_FNAME, 1, error,
-			     sizeof(error));
-	if (IS_ERR(L)) {
+	lua = xlua_init_at(data_dir_fd, DATA_DIR_CONFIG_FNAME, 1, error,
+			   sizeof(error));
+	if (IS_ERR(lua)) {
 		fprintf(stderr, "Error: failed to read config: %s\n", error);
-		return PTR_ERR(L);
+		return PTR_ERR(lua);
 	}
 
+	L = xlua_get_state(lua);
+
 	switch (lua_type(L, -1)) {
 		case LUA_TTABLE:
 			/* got a table - process it */

          
@@ 156,7 159,7 @@ int load_config(int data_dir_fd, struct 
 	ret = 0;
 
 err_lua:
-	lua_close(L);
+	xlua_put_and_close(lua, L);
 
 	if (ret)
 		fprintf(stderr, "Error: failed to load config: %s\n",

          
M hlog/contest.c +10 -10
@@ 176,8 176,8 @@ static void stop_listeners(const char *g
 	announce_stop();
 }
 
-static int do_export(lua_State *L, const struct contest_params *params,
-		     const char *outdir)
+static int do_export(struct xlua_state *lua,
+		     const struct contest_params *params, const char *outdir)
 {
 	char error[128];
 	int ret;

          
@@ 185,7 185,7 @@ static int do_export(lua_State *L, const
 	printf("Exporting %s entries to %s...\n", str_cstr(params->name),
 	       outdir);
 
-	ret = script_event_export(L, outdir, error, sizeof(error));
+	ret = script_event_export(lua, outdir, error, sizeof(error));
 	if (ret)
 		fprintf(stderr, "Error: Failed to export log: %s\n",
 			error);

          
@@ 206,7 206,7 @@ int main(int argc, char **argv)
 	uint32_t rig_model;
 	uint16_t rig_port;
 	const char *gpsd_port;
-	lua_State *L;
+	struct xlua_state *lua;
 	char *export;
 	int ret;
 	int opt;

          
@@ 383,18 383,18 @@ int main(int argc, char **argv)
 			goto err_listen;
 	}
 
-	L = script_load(&wfields, &nwfields, &contest_params);
-	if (IS_ERR(L)) {
-		ret = PTR_ERR(L);
+	lua = script_load(&wfields, &nwfields, &contest_params);
+	if (IS_ERR(lua)) {
+		ret = PTR_ERR(lua);
 		goto err_free;
 	}
 
 	if (!export)
-		ret = contest_run(L, wfields, nwfields);
+		ret = contest_run(lua, wfields, nwfields);
 	else
-		ret = do_export(L, &contest_params, export);
+		ret = do_export(lua, &contest_params, export);
 
-	lua_close(L);
+	xlua_close(lua);
 
 err_free:
 	if (!export)

          
M hlog/contest/contest.h +5 -4
@@ 42,7 42,8 @@ struct contest_params {
 };
 
 extern int contest_init(struct contest_params *contest_params);
-extern int contest_run(lua_State *L, struct xfield_def *wfields, size_t nwfields);
+extern int contest_run(struct xlua_state *lua, struct xfield_def *wfields,
+		       size_t nwfields);
 extern void contest_cleanup(void);
 
 /*

          
@@ 56,13 57,13 @@ extern struct recent recent_list; /* all
 /*
  * interactive form UI
  */
-extern void ui_work_dispatch(lua_State *L, int ch, struct qso **qso);
-extern void ui_current_dispatch(lua_State *L, int ch, struct qso **qso);
+extern void ui_work_dispatch(struct xlua_state *lua, int ch, struct qso **qso);
+extern void ui_current_dispatch(struct xlua_state *lua, int ch, struct qso **qso);
 
 /*
  * contact handling
  */
-extern int init_contact(lua_State *L, struct qso **qso, char *error,
+extern int init_contact(struct xlua_state *lua, struct qso **qso, char *error,
 			size_t errlen);
 extern int save_qso_and_update_index(struct qso *qso, const char *source);
 

          
M hlog/contest/script.c +37 -14
@@ 313,9 313,10 @@ int script_exists(const struct contest_p
 	return xlua_script_find(file, NULL, NULL);
 }
 
-lua_State *script_load(struct xfield_def **fields, size_t *nfields,
-		       const struct contest_params *params)
+struct xlua_state *script_load(struct xfield_def **fields, size_t *nfields,
+			       const struct contest_params *params)
 {
+	struct xlua_state *lua;
 	char error[128];
 	char file[128];
 	lua_State *L;

          
@@ 326,12 327,14 @@ lua_State *script_load(struct xfield_def
 
 	snprintf(file, sizeof(file), "contests/%s.lua", str_cstr(params->name));
 
-	L = xlua_init_script(file, 1, error, sizeof(error));
-	if (IS_ERR(L)) {
+	lua = xlua_init_script(file, 1, error, sizeof(error));
+	if (IS_ERR(lua)) {
 		printf("Error: failed to initialize lua: %s\n", error);
-		return L;
+		return lua;
 	}
 
+	L = xlua_get_state(lua);
+
 	if (lua_type(L, -1) != LUA_TTABLE) {
 		fprintf(stderr, "Error: contest script init failed: "
 			"expected table, got %s\n",

          
@@ 382,10 385,12 @@ lua_State *script_load(struct xfield_def
 
 	ASSERT_LUA_STACK(L, 0);
 
-	return L;
+	xlua_put_state(lua, L);
+
+	return lua;
 
 err:
-	lua_close(L);
+	xlua_put_and_close(lua, L);
 
 	return ERR_PTR(ret);
 }

          
@@ 444,16 449,20 @@ static int script_event_startup(lua_Stat
 	return ret;
 }
 
-int script_event_export(lua_State *L, const char *outdir, char *error,
+int script_event_export(struct xlua_state *lua, const char *outdir, char *error,
 			size_t errlen)
 {
+	lua_State *L;
 	int type;
 	int ret;
 
+	L = xlua_get_state(lua);
+
 	type = lua_getfield(L, LUA_REGISTRYINDEX, EVENT_EXPORT);
 	if (type == LUA_TNIL) {
 		lua_pop(L, 1);
 		ASSERT_LUA_STACK(L, 0);
+		xlua_put_state(lua, L);
 		snprintf(error, errlen, "Contest doesn't define an export "
 			 "function");
 		return -ENOTSUP;

          
@@ 465,10 474,12 @@ int script_event_export(lua_State *L, co
 
 	ASSERT_LUA_STACK(L, 0);
 
+	xlua_put_state(lua, L);
+
 	return ret;
 }
 
-int script_event_field_change(lua_State *L, struct qso *qso,
+int script_event_field_change(struct xlua_state *lua, struct qso *qso,
 			      struct xform *form,
 			      char *error, size_t errlen,
 			      void (*fcb)(struct xform *, const char *,

          
@@ 476,14 487,18 @@ int script_event_field_change(lua_State 
 			      void (*acb)(struct xform *, const char *,
 					  const char *))
 {
+	lua_State *L;
 	size_t i;
 	int type;
 	int ret;
 
+	L = xlua_get_state(lua);
+
 	type = lua_getfield(L, LUA_REGISTRYINDEX, EVENT_FIELD_CHANGED);
 	if (type == LUA_TNIL) {
 		lua_pop(L, 1);
 		ASSERT_LUA_STACK(L, 0);
+		xlua_put_state(lua, L);
 		return 0;
 	}
 

          
@@ 530,19 545,25 @@ int script_event_field_change(lua_State 
 out:
 	ASSERT_LUA_STACK(L, 0);
 
+	xlua_put_state(lua, L);
+
 	return ret;
 }
 
-static int event_qso(lua_State *L, struct qso *qso, const char *event,
+static int event_qso(struct xlua_state *lua, struct qso *qso, const char *event,
 		     char *error, size_t errlen)
 {
+	lua_State *L;
 	int type;
 	int ret;
 
+	L = xlua_get_state(lua);
+
 	type = lua_getfield(L, LUA_REGISTRYINDEX, event);
 	if (type == LUA_TNIL) {
 		lua_pop(L, 1);
 		ASSERT_LUA_STACK(L, 0);
+		xlua_put_state(lua, L);
 		return 0;
 	}
 

          
@@ 550,17 571,19 @@ static int event_qso(lua_State *L, struc
 
 	ASSERT_LUA_STACK(L, 0);
 
+	xlua_put_state(lua, L);
+
 	return ret;
 }
 
-int script_event_qso_init(lua_State *L, struct qso *qso,
+int script_event_qso_init(struct xlua_state *lua, struct qso *qso,
 			  char *error, size_t errlen)
 {
-	return event_qso(L, qso, EVENT_QSO_INIT, error, errlen);
+	return event_qso(lua, qso, EVENT_QSO_INIT, error, errlen);
 }
 
-int script_event_qso_done(lua_State *L, struct qso *qso,
+int script_event_qso_done(struct xlua_state *lua, struct qso *qso,
 			  char *error, size_t errlen)
 {
-	return event_qso(L, qso, EVENT_QSO_DONE, error, errlen);
+	return event_qso(lua, qso, EVENT_QSO_DONE, error, errlen);
 }

          
M hlog/contest/script.h +9 -8
@@ 1,5 1,5 @@ 
 /*
- * Copyright (c) 2020-2022 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ * Copyright (c) 2020-2022,2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal

          
@@ 28,22 28,23 @@ 
 #include "../common/xform.h"
 
 extern int script_exists(const struct contest_params *params);
-extern lua_State *script_load(struct xfield_def **fields, size_t *nfields,
-			      const struct contest_params *params);
+extern struct xlua_state *script_load(struct xfield_def **fields,
+				      size_t *nfields,
+				      const struct contest_params *params);
 
 /* events */
-extern int script_event_field_change(lua_State *L, struct qso *qso,
+extern int script_event_field_change(struct xlua_state *lua, struct qso *qso,
 				     struct xform *form, char *error,
 				     size_t errlen,
 				     void (*fcb)(struct xform *, const char *,
 						 const char *),
 				     void (*acb)(struct xform *, const char *,
 						 const char *));
-extern int script_event_qso_init(lua_State *L, struct qso *qso,
+extern int script_event_qso_init(struct xlua_state *lua, struct qso *qso,
 				 char *error, size_t errlen);
-extern int script_event_qso_done(lua_State *L, struct qso *qso,
+extern int script_event_qso_done(struct xlua_state *lua, struct qso *qso,
 				 char *error, size_t errlen);
-extern int script_event_export(lua_State *L, const char *outdir, char *error,
-			       size_t errlen);
+extern int script_event_export(struct xlua_state *lua, const char *outdir,
+			       char *error, size_t errlen);
 
 #endif

          
M hlog/contest/ui-work.c +13 -13
@@ 65,7 65,7 @@ static void wrapped_set_annunciator_colo
 	}
 }
 
-static void field_changed(lua_State *L, struct qso *qso)
+static void field_changed(struct xlua_state *lua, struct qso *qso)
 {
 	struct xform *form = &work; /* for simplicity */
 	char error[128];

          
@@ 75,7 75,7 @@ static void field_changed(lua_State *L, 
 	if (!qso->start.have)
 		ASSERT0(qso_set_datetime(qso, true, NULL));
 
-	ret = script_event_field_change(L, qso, form,
+	ret = script_event_field_change(lua, qso, form,
 					error, sizeof(error),
 					wrapped_set_field_buffer,
 					wrapped_set_annunciator_color);

          
@@ 85,7 85,7 @@ static void field_changed(lua_State *L, 
 	}
 }
 
-static void abort_qso(lua_State *L, struct qso **_qso)
+static void abort_qso(struct xlua_state *lua, struct qso **_qso)
 {
 	struct xform *form = &work; /* for simplicity */
 	char error[128];

          
@@ 102,14 102,14 @@ static void abort_qso(lua_State *L, stru
 	xform_driver(form, XFORM_REQ_FIRST_FIELD);
 
 	/* initialize the qso */
-	ret = init_contact(L, _qso, error, sizeof(error));
+	ret = init_contact(lua, _qso, error, sizeof(error));
 	if (ret) {
 		msg_print(MSG_SCRIPT, CE_ERROR, "%s", error);
 		status_display_error("%s", error);
 	}
 }
 
-static void end_qso(lua_State *L, struct qso **_qso)
+static void end_qso(struct xlua_state *lua, struct qso **_qso)
 {
 	struct xform *form = &work; /* for simplicity */
 	struct qso *qso = *_qso;

          
@@ 117,7 117,7 @@ static void end_qso(lua_State *L, struct
 	size_t i;
 	int ret;
 
-	ret = script_event_qso_done(L, qso, error, sizeof(error));
+	ret = script_event_qso_done(lua, qso, error, sizeof(error));
 	if (ret)
 		goto err;
 

          
@@ 153,7 153,7 @@ static void end_qso(lua_State *L, struct
 	xform_driver(form, XFORM_REQ_FIRST_FIELD);
 
 	/* start the next qso */
-	ret = init_contact(L, _qso, error, sizeof(error));
+	ret = init_contact(lua, _qso, error, sizeof(error));
 	if (ret)
 		msg_print(MSG_SCRIPT, CE_ERROR, "%s", error);
 

          
@@ 164,7 164,7 @@ err:
 	status_display_error("%s", error);
 }
 
-void ui_work_dispatch(lua_State *L, int ch, struct qso **qso)
+void ui_work_dispatch(struct xlua_state *lua, int ch, struct qso **qso)
 {
 	struct xform *form = &work; /* for simplicity */
 

          
@@ 178,33 178,33 @@ void ui_work_dispatch(lua_State *L, int 
 		case '\b':
 		case 127:
 			xform_driver(form, XFORM_REQ_DEL_PREV);
-			field_changed(L, *qso); /* inform script */
+			field_changed(lua, *qso); /* inform script */
 			recent_invalidate(&recent_work_list);
 			break;
 
 		case KEY_DC:
 			xform_driver(form, XFORM_REQ_DEL_THIS);
-			field_changed(L, *qso); /* inform script */
+			field_changed(lua, *qso); /* inform script */
 			recent_invalidate(&recent_work_list);
 			break;
 
 		/* navigate between forms */
 		case KEY_ENTER:
 		case '\n':
-			end_qso(L, qso);
+			end_qso(lua, qso);
 			recent_invalidate(&recent_work_list);
 			recent_invalidate(&recent_list);
 			break;
 
 		case '\x1b': /* escape */
-			abort_qso(L, qso);
+			abort_qso(lua, qso);
 			recent_invalidate(&recent_work_list);
 			break;
 
 		default:
 			if (!iscntrl(ch)) {
 				xform_driver(form, ch);
-				field_changed(L, *qso); /* inform script */
+				field_changed(lua, *qso); /* inform script */
 				recent_invalidate(&recent_work_list);
 			} else {
 				beep();

          
M hlog/contest/ui.c +7 -5
@@ 323,7 323,8 @@ static struct xform_palette palette = {
 /*
  * contact setup
  */
-int init_contact(lua_State *L, struct qso **qso, char *error, size_t errlen)
+int init_contact(struct xlua_state *lua, struct qso **qso, char *error,
+		 size_t errlen)
 {
 	struct qso *tmp;
 

          
@@ 342,7 343,7 @@ int init_contact(lua_State *L, struct qs
 	xuuid_unparse(&tmp->uuid, tmp->uuid_str);
 
 	/* let the script finish up setup */
-	return script_event_qso_init(L, tmp, error, errlen);
+	return script_event_qso_init(lua, tmp, error, errlen);
 }
 
 static void __get_qso_ts(const struct time *ts, size_t field)

          
@@ 941,7 942,8 @@ void contest_cleanup(void)
 	MXDESTROY(&location_lock);
 }
 
-int contest_run(lua_State *L, struct xfield_def *_wfields, size_t _nwfields)
+int contest_run(struct xlua_state *lua, struct xfield_def *_wfields,
+		size_t _nwfields)
 {
 	char error[128];
 	uint64_t gen;

          
@@ 954,7 956,7 @@ int contest_run(lua_State *L, struct xfi
 	tweak_script_colors();
 
 	/* set up the current contact struct qso */
-	ret = init_contact(L, &contact, error, sizeof(error));
+	ret = init_contact(lua, &contact, error, sizeof(error));
 	if (ret) {
 		fprintf(stderr, "Error: Failed to initialize QSO: %s\n",
 			error);

          
@@ 996,7 998,7 @@ int contest_run(lua_State *L, struct xfi
 		if (ch != ERR) {
 			status_display_error(""); /* clear the error string */
 
-			ui_work_dispatch(L, ch, &contact);
+			ui_work_dispatch(lua, ch, &contact);
 		}
 
 		draw_location();

          
M hlog/hlog.c +7 -4
@@ 1978,20 1978,23 @@ static int load_index(void)
 static void print_stats(void)
 {
 	struct index_iter_ctx ctx;
+	struct xlua_state *lua;
+	lua_State *L;
 	struct qso *qso;
 	char error[128];
-	lua_State *L;
 	bool failed;
 	int ret;
 
 	printf("Global call statistics:\n");
 
-	L = xlua_init_script("startup-stats.lua", 0, error, sizeof(error));
-	if (IS_ERR(L)) {
+	lua = xlua_init_script("startup-stats.lua", 0, error, sizeof(error));
+	if (IS_ERR(lua)) {
 		printf("Error: failed to initialize lua: %s\n", error);
 		return;
 	}
 
+	L = xlua_get_state(lua);
+
 	/* iterate over all the QSOs */
 	for (qso = index_iter_history_start(&ctx, true), failed = false;
 	     qso;

          
@@ 2012,7 2015,7 @@ static void print_stats(void)
 	if (ret)
 		printf("Error: %s\n", error);
 
-	lua_close(L);
+	xlua_put_and_close(lua, L);
 }
 
 static void state_free(void)

          
M run-script/run-script.c +8 -5
@@ 1,5 1,5 @@ 
 /*
- * Copyright (c) 2020-2021 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ * Copyright (c) 2020-2021,2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal

          
@@ 37,21 37,24 @@ static int data_dir_fd;
 
 static void process(const char *scriptname)
 {
+	struct xlua_state *lua;
+	lua_State *L;
 	char error[128];
-	lua_State *L;
 	int ret;
 
-	L = xlua_init(scriptname, 0, error, sizeof(error));
-	if (IS_ERR(L)) {
+	lua = xlua_init(scriptname, 0, error, sizeof(error));
+	if (IS_ERR(lua)) {
 		printf("Error: failed to initialize lua: %s\n", error);
 		return;
 	}
 
+	L = xlua_get_state(lua);
+
 	ret = xlua_call(L, "main", 0, 0, error, sizeof(error));
 	if (ret)
 		printf("Error: failed to execute lua: %s\n", error);
 
-	lua_close(L);
+	xlua_put_and_close(lua, L);
 }
 
 static void usage(const char *prog)

          
M xlua/include/hlog-lua/xlua.h +21 -7
@@ 1,5 1,5 @@ 
 /*
- * Copyright (c) 2020-2022 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ * Copyright (c) 2020-2022,2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal

          
@@ 48,11 48,19 @@ 
 #define COPY_ERROR(L, idx, err, errlen) \
 	strcpy_safe(err, lua_tostring((L), (idx)), (errlen))
 
+struct xlua_state;
+
 extern int lua2errno(int e);
-extern lua_State *xlua_init_at(int dirfd, const char *script_fname, int nresults,
-			       char *error, size_t errlen);
-extern lua_State *xlua_init_script(const char *script_name, int nresults,
-				   char *error, size_t errlen);
+extern struct xlua_state *xlua_init_at(int dirfd, const char *script_fname,
+				       int nresults, char *error,
+				       size_t errlen);
+extern struct xlua_state *xlua_init_script(const char *script_name,
+					   int nresults, char *error,
+					   size_t errlen);
+extern void xlua_close(struct xlua_state *lua);
+
+extern lua_State *xlua_get_state(struct xlua_state *lua);
+extern void xlua_put_state(struct xlua_state *lua, lua_State *L);
 
 /* return raw script data */
 extern int xlua_script_find(const char *name, const uint8_t **data_r,

          
@@ 86,12 94,18 @@ extern int xlua_pushqso(lua_State *L, st
 extern int xlua_pushtime(lua_State *L, struct time *time, bool ro,
 			 char *error, size_t errlen);
 
-static inline lua_State *xlua_init(const char *script_fname, int nresults,
-				   char *error, size_t errlen)
+static inline struct xlua_state *xlua_init(const char *script_fname,
+					   int nresults, char *error,
+					   size_t errlen)
 {
 	return xlua_init_at(AT_FDCWD, script_fname, nresults, error, errlen);
 }
 
+static inline void xlua_put_and_close(struct xlua_state *lua, lua_State *L)
+{
+	xlua_close(lua);
+}
+
 static inline bool xlua_isstringornil(lua_State *L, int idx)
 {
 	return lua_isnil(L, idx) || lua_isstring(L, idx);

          
M xlua/xlua-impl.h +4 -0
@@ 28,6 28,10 @@ 
 #include <hlog/qso.h>
 #include <hlog-lua/xlua.h>
 
+struct xlua_state {
+	lua_State *L;
+};
+
 extern int xlua_load_script_internal(lua_State *L, const char *scriptname,
 				     char *error, size_t errlen);
 extern void xlua_merge_lua_funcs(lua_State *L, const char *scriptname);

          
M xlua/xlua.c +49 -18
@@ 223,50 223,64 @@ void xlua_merge_lua_funcs(lua_State *L, 
 	ASSERT_LUA_STACK(L, orig_top);
 }
 
-static lua_State *__xlua_init(const char *name, const uint8_t *data,
-			      size_t size, int nresults, char *error,
-			      size_t errlen)
+static struct xlua_state *__xlua_init(const char *name, const uint8_t *data,
+				      size_t size, int nresults, char *error,
+				      size_t errlen)
 {
+	struct xlua_state *lua;
 	struct buffer buf;
 	lua_State *L;
 	int ret;
 
 	buffer_init_static(&buf, data, size, size, false);
 
-	L = luaL_newstate();
-	if (!L) {
-		strcpy_safe(error, "failed to allocate lua state", errlen);
+	lua = malloc(sizeof(struct xlua_state));
+	if (!lua) {
+		strcpy_safe(error, "failed to allocate xlua state", errlen);
 		return ERR_PTR(-ENOMEM);
 	}
 
+	lua->L = luaL_newstate();
+	if (!lua->L) {
+		strcpy_safe(error, "failed to allocate lua state", errlen);
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	L = lua->L;
+
 	luaL_openlibs(L);
 
 	xlua_register(L);
 
 	ret = lua_load(L, xlua_init_reader, &buf, name, NULL);
 	if (ret != LUA_OK)
-		goto err;
+		goto err_free;
 
 	ret = lua_pcall(L, 0, nresults, 0);
 	if (ret)
-		goto err;
+		goto err_free;
 
 	ASSERT_LUA_STACK(L, nresults);
 
-	return L;
 
-err:
+	return lua;
+
+err_free:
 	COPY_ERROR(L, -1, error, errlen);
 
 	lua_close(L);
 
+err:
+	free(lua);
+
 	return ERR_PTR(lua2errno(ret));
 }
 
-lua_State *xlua_init_at(int dirfd, const char *fname, int nresults, char *error,
-			size_t errlen)
+struct xlua_state *xlua_init_at(int dirfd, const char *fname, int nresults,
+				char *error, size_t errlen)
 {
-	lua_State *L;
+	struct xlua_state *lua;
 	char *raw;
 	size_t size;
 

          
@@ 281,16 295,16 @@ lua_State *xlua_init_at(int dirfd, const
 		return ERR_CAST(raw);
 	}
 
-	L = __xlua_init(fname, (const uint8_t *) raw, size, nresults, error,
-			errlen);
+	lua = __xlua_init(fname, (const uint8_t *) raw, size, nresults, error,
+			  errlen);
 
 	free(raw);
 
-	return L;
+	return lua;
 }
 
-lua_State *xlua_init_script(const char *scriptname, int nresults, char *error,
-			    size_t errlen)
+struct xlua_state *xlua_init_script(const char *scriptname, int nresults,
+				    char *error, size_t errlen)
 {
 	const uint8_t *data;
 	size_t size;

          
@@ 310,6 324,23 @@ lua_State *xlua_init_script(const char *
 	return __xlua_init(scriptname, data, size, nresults, error, errlen);
 }
 
+void xlua_close(struct xlua_state *lua)
+{
+	lua_close(lua->L);
+
+	free(lua);
+}
+
+lua_State *xlua_get_state(struct xlua_state *lua)
+{
+	return lua->L;
+}
+
+void xlua_put_state(struct xlua_state *lua, lua_State *L)
+{
+	ASSERT3P(lua->L, ==, L);
+}
+
 int xlua_call(lua_State *L, const char *funcname, int nargs, int nresults,
 	      char *error, size_t errlen)
 {