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