1#include "mod_magnet_cache.h"
2#include "stat_cache.h"
3
4#include <stdlib.h>
5#include <time.h>
6#include <assert.h>
7
8#ifdef HAVE_LUA_H
9#include <lualib.h>
10#include <lauxlib.h>
11
12static script *script_init() {
13	script *sc;
14
15	sc = calloc(1, sizeof(*sc));
16	sc->name = buffer_init();
17	sc->etag = buffer_init();
18
19	return sc;
20}
21
22static void script_free(script *sc) {
23	if (!sc) return;
24
25	lua_pop(sc->L, 1); /* the function copy */
26
27	buffer_free(sc->name);
28	buffer_free(sc->etag);
29
30	lua_close(sc->L);
31
32	free(sc);
33}
34
35script_cache *script_cache_init() {
36	script_cache *p;
37
38	p = calloc(1, sizeof(*p));
39
40	return p;
41}
42
43void script_cache_free(script_cache *p) {
44	size_t i;
45
46	if (!p) return;
47
48	for (i = 0; i < p->used; i++) {
49		script_free(p->ptr[i]);
50	}
51
52	free(p->ptr);
53
54	free(p);
55}
56
57lua_State *script_cache_get_script(server *srv, connection *con, script_cache *cache, buffer *name) {
58	size_t i;
59	script *sc = NULL;
60	stat_cache_entry *sce;
61
62	for (i = 0; i < cache->used; i++) {
63		sc = cache->ptr[i];
64
65		if (buffer_is_equal(name, sc->name)) {
66			sc->last_used = time(NULL);
67
68			/* oops, the script failed last time */
69
70			if (lua_gettop(sc->L) == 0) break;
71
72			if (HANDLER_ERROR == stat_cache_get_entry(srv, con, sc->name, &sce)) {
73				lua_pop(sc->L, 1); /* pop the old function */
74				break;
75			}
76
77			if (!buffer_is_equal(sce->etag, sc->etag)) {
78				/* the etag is outdated, reload the function */
79				lua_pop(sc->L, 1);
80				break;
81			}
82
83			force_assert(lua_isfunction(sc->L, -1));
84			lua_pushvalue(sc->L, -1); /* copy the function-reference */
85
86			return sc->L;
87		}
88
89		sc = NULL;
90	}
91
92	/* if the script was script already loaded but either got changed or
93	 * failed to load last time */
94	if (sc == NULL) {
95		sc = script_init();
96
97		if (cache->size == 0) {
98			cache->size = 16;
99			cache->ptr = malloc(cache->size * sizeof(*(cache->ptr)));
100		} else if (cache->used == cache->size) {
101			cache->size += 16;
102			cache->ptr = realloc(cache->ptr, cache->size * sizeof(*(cache->ptr)));
103		}
104
105		cache->ptr[cache->used++] = sc;
106
107		buffer_copy_buffer(sc->name, name);
108
109		sc->L = luaL_newstate();
110		luaL_openlibs(sc->L);
111	}
112
113	sc->last_used = time(NULL);
114
115	if (0 != luaL_loadfile(sc->L, name->ptr)) {
116		/* oops, an error, return it */
117
118		return sc->L;
119	}
120
121	if (HANDLER_GO_ON == stat_cache_get_entry(srv, con, sc->name, &sce)) {
122		buffer_copy_buffer(sc->etag, sce->etag);
123	}
124
125	/**
126	 * pcall() needs the function on the stack
127	 *
128	 * as pcall() will pop the script from the stack when done, we have to
129	 * duplicate it here
130	 */
131	force_assert(lua_isfunction(sc->L, -1));
132	lua_pushvalue(sc->L, -1); /* copy the function-reference */
133
134	return sc->L;
135}
136
137#endif
138