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