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 lua_close(sc->L); 26 free(sc->name.ptr); 27 free(sc->etag.ptr); 28 free(sc); 29 } 30 31 #if 0 32 script_cache *script_cache_init(void) 33 { 34 script_cache *p = calloc(1, sizeof(script_cache)); 35 force_assert(p); 36 return p; 37 } 38 #endif 39 40 void script_cache_free_data(script_cache *p) 41 { 42 if (!p) return; 43 for (uint32_t i = 0; i < p->used; ++i) 44 script_free(p->ptr[i]); 45 free(p->ptr); 46 } 47 48 __attribute_cold__ 49 __attribute_noinline__ 50 static lua_State *script_cache_load_script(script * const sc, int etag_flags) 51 { 52 /* read file and use luaL_loadbuffer() 53 * eliminate TOC-TOU race w/ independent stat() in stat_cache_get_entry() */ 54 55 stat_cache_entry * const sce = stat_cache_get_entry_open(&sc->name, 1); 56 buffer_clear(&sc->etag); 57 if (NULL == sce || sce->fd < 0) { 58 /*(sce->fd < 0 might indicate empty file, which is not a valid script)*/ 59 if (NULL != sce) errno = EBADF; 60 return NULL; 61 } 62 const buffer * const etag = stat_cache_etag_get(sce, etag_flags); 63 if (etag) 64 buffer_copy_buffer(&sc->etag, etag); 65 66 const off_t sz = sce->st.st_size; 67 char * const buf = malloc(sz); 68 force_assert(buf); 69 70 ssize_t rd = 0; 71 off_t off = 0; 72 do { 73 rd = read(sce->fd, buf+off, (size_t)(sz-off)); 74 } while (rd > 0 ? (off += rd) != sz : rd < 0 && errno == EINTR); 75 if (off != sz) { /*(file truncated?)*/ 76 if (rd >= 0) errno = EIO; 77 free(buf); 78 return NULL; 79 } 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__ 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 == cache->size) { 101 cache->size += 16; 102 cache->ptr = realloc(cache->ptr, cache->size * sizeof(*(cache->ptr))); 103 force_assert(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) == 2);*/ 129 /*force_assert(lua_isfunction(sc->L, -2));*/ 130 131 stat_cache_entry * const sce = stat_cache_get_entry(&sc->name); 132 if (NULL == sce) { 133 lua_pop(sc->L, 2); /* pop the old function and lighty table */ 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, 2); /* pop the old function and lighty table */ 143 return script_cache_load_script(sc, etag_flags); 144 } 145 146 return sc->L; 147 } 148