xref: /lighttpd1.4/src/mod_magnet_cache.c (revision 4e5b822b)
1 #include "first.h"
2 
3 #include "mod_magnet_cache.h"
4 #include "stat_cache.h"
5 
6 #include <errno.h>
7 #include <stdlib.h>
8 #include <unistd.h>     /* read() */
9 
10 #include <lualib.h>
11 #include <lauxlib.h>
12 
13 __attribute_cold__
14 static script *script_init(void)
15 {
16     script *const sc = calloc(1, sizeof(*sc));
17     force_assert(sc);
18     return sc;
19 }
20 
21 __attribute_cold__
22 static void script_free(script *sc)
23 {
24     if (!sc) return;
25     if (lua_gettop(sc->L) != 0)
26         lua_pop(sc->L, 1); /* the function copy */
27     lua_close(sc->L);
28     free(sc->name.ptr);
29     free(sc->etag.ptr);
30     free(sc);
31 }
32 
33 #if 0
34 script_cache *script_cache_init(void)
35 {
36     script_cache *p = calloc(1, sizeof(script_cache));
37     force_assert(p);
38     return p;
39 }
40 #endif
41 
42 void script_cache_free_data(script_cache *p)
43 {
44     if (!p) return;
45     for (uint32_t i = 0; i < p->used; ++i)
46         script_free(p->ptr[i]);
47     free(p->ptr);
48 }
49 
50 __attribute_cold__
51 __attribute_noinline__
52 static lua_State *script_cache_load_script(script * const sc, int etag_flags)
53 {
54     /* read file and use luaL_loadbuffer()
55      * eliminate TOC-TOU race w/ independent stat() in stat_cache_get_entry() */
56 
57     stat_cache_entry * const sce = stat_cache_get_entry_open(&sc->name, 1);
58     buffer_clear(&sc->etag);
59     if (NULL == sce || sce->fd < 0) {
60         /*(sce->fd < 0 might indicate empty file, which is not a valid script)*/
61         if (NULL != sce) errno = EBADF;
62         return NULL;
63     }
64     const buffer * const etag = stat_cache_etag_get(sce, etag_flags);
65     if (etag)
66         buffer_copy_buffer(&sc->etag, etag);
67 
68     const off_t sz = sce->st.st_size;
69     char * const buf = malloc(sz);
70     force_assert(buf);
71 
72     ssize_t rd = 0;
73     off_t off = 0;
74     do {
75         rd = read(sce->fd, buf+off, (size_t)(sz-off));
76     } while (rd > 0 ? (off += rd) != sz : rd < 0 && errno == EINTR);
77     if (off != sz) { /*(file truncated?)*/
78         if (rd >= 0) errno = EIO;
79         return NULL;
80     }
81 
82     int rc = luaL_loadbuffer(sc->L, buf, (size_t)sz, sc->name.ptr);
83     free(buf);
84 
85     if (0 != rc) {
86         /* oops, an error, return it */
87         return sc->L;
88     }
89 
90     force_assert(lua_isfunction(sc->L, -1));
91     return sc->L;
92 }
93 
94 __attribute_cold__
95 __attribute_nonnull__
96 __attribute_returns_nonnull__
97 static script *script_cache_new_script(script_cache * const cache, const buffer * const name)
98 {
99     script * const sc = script_init();
100 
101     if (cache->used == cache->size) {
102         cache->size += 16;
103         cache->ptr = realloc(cache->ptr, cache->size * sizeof(*(cache->ptr)));
104     }
105     cache->ptr[cache->used++] = sc;
106 
107     buffer_copy_buffer(&sc->name, name);
108     sc->L = luaL_newstate();
109     luaL_openlibs(sc->L);
110     return sc;
111 }
112 
113 script *script_cache_get_script(script_cache *cache, const buffer *name)
114 {
115     for (uint32_t i = 0; i < cache->used; ++i) {
116         script * const sc = cache->ptr[i];
117         if (buffer_is_equal(&sc->name, name))
118             return sc;
119     }
120     return script_cache_new_script(cache, name);
121 }
122 
123 lua_State *script_cache_check_script(script * const sc, int etag_flags)
124 {
125     if (lua_gettop(sc->L) == 0)
126         return script_cache_load_script(sc, etag_flags);
127 
128     force_assert(lua_gettop(sc->L) == 1);
129     force_assert(lua_isfunction(sc->L, -1));
130 
131     stat_cache_entry * const sce = stat_cache_get_entry(&sc->name);
132     if (NULL == sce) {
133         lua_pop(sc->L, 1); /* pop the old function */
134         return script_cache_load_script(sc, etag_flags);
135     }
136 
137     const buffer * const etag = stat_cache_etag_get(sce, etag_flags);
138     if (NULL == etag || !buffer_is_equal(&sc->etag, etag)) {
139         if (0 == etag_flags)
140             return sc->L;
141         /* the etag is outdated, reload the function */
142         lua_pop(sc->L, 1);
143         return script_cache_load_script(sc, etag_flags);
144     }
145 
146     return sc->L;
147 }
148