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 return ck_calloc(1, sizeof(script)); 18 } 19 20 __attribute_cold__ 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 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__ 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__ 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 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 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