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