1/**
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17#include "mod_lua.h"
18#include "http_log.h"
19#include "apr_uuid.h"
20#include "lua_config.h"
21#include "apr_file_info.h"
22#include "mod_auth.h"
23
24APLOG_USE_MODULE(lua);
25
26#ifndef AP_LUA_MODULE_EXT
27#if defined(NETWARE)
28#define AP_LUA_MODULE_EXT ".nlm"
29#elif defined(WIN32)
30#define AP_LUA_MODULE_EXT ".dll"
31#elif (defined(__hpux__) || defined(__hpux)) && !defined(__ia64)
32#define AP_LUA_MODULE_EXT ".sl"
33#else
34#define AP_LUA_MODULE_EXT ".so"
35#endif
36#endif
37
38#if APR_HAS_THREADS
39    apr_thread_mutex_t *ap_lua_mutex;
40
41void ap_lua_init_mutex(apr_pool_t *pool, server_rec *s)
42{
43    apr_thread_mutex_create(&ap_lua_mutex, APR_THREAD_MUTEX_DEFAULT, pool);
44}
45#endif
46
47/* forward dec'l from this file */
48
49#if 0
50static void pstack_dump(lua_State *L, apr_pool_t *r, int level,
51                        const char *msg)
52{
53    int i;
54    int top = lua_gettop(L);
55
56    ap_log_perror(APLOG_MARK, level, 0, r, "Lua Stack Dump: [%s]", msg);
57
58    for (i = 1; i <= top; i++) {
59        int t = lua_type(L, i);
60        switch (t) {
61        case LUA_TSTRING:{
62                ap_log_perror(APLOG_MARK, level, 0, r,
63                              "%d:  '%s'", i, lua_tostring(L, i));
64                break;
65            }
66        case LUA_TUSERDATA:{
67                ap_log_perror(APLOG_MARK, level, 0, r, "%d:  userdata", i);
68                break;
69            }
70        case LUA_TLIGHTUSERDATA:{
71                ap_log_perror(APLOG_MARK, level, 0, r, "%d:  lightuserdata",
72                              i);
73                break;
74            }
75        case LUA_TNIL:{
76                ap_log_perror(APLOG_MARK, level, 0, r, "%d:  NIL", i);
77                break;
78            }
79        case LUA_TNONE:{
80                ap_log_perror(APLOG_MARK, level, 0, r, "%d:  None", i);
81                break;
82            }
83        case LUA_TBOOLEAN:{
84                ap_log_perror(APLOG_MARK, level, 0, r,
85                              "%d:  %s", i, lua_toboolean(L,
86                                                          i) ? "true" :
87                              "false");
88                break;
89            }
90        case LUA_TNUMBER:{
91                ap_log_perror(APLOG_MARK, level, 0, r,
92                              "%d:  %g", i, lua_tonumber(L, i));
93                break;
94            }
95        case LUA_TTABLE:{
96                ap_log_perror(APLOG_MARK, level, 0, r, "%d:  <table>", i);
97                break;
98            }
99        case LUA_TTHREAD:{
100                ap_log_perror(APLOG_MARK, level, 0, r, "%d:  <thread>", i);
101                break;
102            }
103        case LUA_TFUNCTION:{
104                ap_log_perror(APLOG_MARK, level, 0, r, "%d:  <function>", i);
105                break;
106            }
107        default:{
108                ap_log_perror(APLOG_MARK, level, 0, r,
109                              "%d:  unknown: [%s]", i, lua_typename(L, i));
110                break;
111            }
112        }
113    }
114}
115#endif
116
117/* BEGIN modules*/
118
119/* BEGIN apache lmodule  */
120
121#define makeintegerfield(L, n) lua_pushinteger(L, n); lua_setfield(L, -2, #n)
122
123void ap_lua_load_apache2_lmodule(lua_State *L)
124{
125    lua_getglobal(L, "package");
126    lua_getfield(L, -1, "loaded");
127    lua_newtable(L);
128    lua_setfield(L, -2, "apache2");
129    lua_setglobal(L, "apache2");
130    lua_pop(L, 1);              /* empty stack */
131
132    lua_getglobal(L, "apache2");
133
134    lua_pushstring(L, ap_get_server_banner());
135    lua_setfield(L, -2, "version");
136
137    makeintegerfield(L, OK);
138    makeintegerfield(L, DECLINED);
139    makeintegerfield(L, DONE);
140    makeintegerfield(L, HTTP_MOVED_TEMPORARILY);
141    makeintegerfield(L, PROXYREQ_NONE);
142    makeintegerfield(L, PROXYREQ_PROXY);
143    makeintegerfield(L, PROXYREQ_REVERSE);
144    makeintegerfield(L, PROXYREQ_RESPONSE);
145    makeintegerfield(L, PROXYREQ_RESPONSE);
146    makeintegerfield(L, AUTHZ_DENIED);
147    makeintegerfield(L, AUTHZ_GRANTED);
148    makeintegerfield(L, AUTHZ_NEUTRAL);
149    makeintegerfield(L, AUTHZ_GENERAL_ERROR);
150    makeintegerfield(L, AUTHZ_DENIED_NO_USER);
151
152    /*
153       makeintegerfield(L, HTTP_CONTINUE);
154       makeintegerfield(L, HTTP_SWITCHING_PROTOCOLS);
155       makeintegerfield(L, HTTP_PROCESSING);
156       makeintegerfield(L, HTTP_OK);
157       makeintegerfield(L, HTTP_CREATED);
158       makeintegerfield(L, HTTP_ACCEPTED);
159       makeintegerfield(L, HTTP_NON_AUTHORITATIVE);
160       makeintegerfield(L, HTTP_NO_CONTENT);
161       makeintegerfield(L, HTTP_RESET_CONTENT);
162       makeintegerfield(L, HTTP_PARTIAL_CONTENT);
163       makeintegerfield(L, HTTP_MULTI_STATUS);
164       makeintegerfield(L, HTTP_ALREADY_REPORTED);
165       makeintegerfield(L, HTTP_IM_USED);
166       makeintegerfield(L, HTTP_MULTIPLE_CHOICES);
167       makeintegerfield(L, HTTP_MOVED_PERMANENTLY);
168       makeintegerfield(L, HTTP_MOVED_TEMPORARILY);
169       makeintegerfield(L, HTTP_SEE_OTHER);
170       makeintegerfield(L, HTTP_NOT_MODIFIED);
171       makeintegerfield(L, HTTP_USE_PROXY);
172       makeintegerfield(L, HTTP_TEMPORARY_REDIRECT);
173       makeintegerfield(L, HTTP_PERMANENT_REDIRECT);
174       makeintegerfield(L, HTTP_BAD_REQUEST);
175       makeintegerfield(L, HTTP_UNAUTHORIZED);
176       makeintegerfield(L, HTTP_PAYMENT_REQUIRED);
177       makeintegerfield(L, HTTP_FORBIDDEN);
178       makeintegerfield(L, HTTP_NOT_FOUND);
179       makeintegerfield(L, HTTP_METHOD_NOT_ALLOWED);
180       makeintegerfield(L, HTTP_NOT_ACCEPTABLE);
181       makeintegerfield(L, HTTP_PROXY_AUTHENTICATION_REQUIRED);
182       makeintegerfield(L, HTTP_REQUEST_TIME_OUT);
183       makeintegerfield(L, HTTP_CONFLICT);
184       makeintegerfield(L, HTTP_GONE);
185       makeintegerfield(L, HTTP_LENGTH_REQUIRED);
186       makeintegerfield(L, HTTP_PRECONDITION_FAILED);
187       makeintegerfield(L, HTTP_REQUEST_ENTITY_TOO_LARGE);
188       makeintegerfield(L, HTTP_REQUEST_URI_TOO_LARGE);
189       makeintegerfield(L, HTTP_UNSUPPORTED_MEDIA_TYPE);
190       makeintegerfield(L, HTTP_RANGE_NOT_SATISFIABLE);
191       makeintegerfield(L, HTTP_EXPECTATION_FAILED);
192       makeintegerfield(L, HTTP_UNPROCESSABLE_ENTITY);
193       makeintegerfield(L, HTTP_LOCKED);
194       makeintegerfield(L, HTTP_FAILED_DEPENDENCY);
195       makeintegerfield(L, HTTP_UPGRADE_REQUIRED);
196       makeintegerfield(L, HTTP_PRECONDITION_REQUIRED);
197       makeintegerfield(L, HTTP_TOO_MANY_REQUESTS);
198       makeintegerfield(L, HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE);
199       makeintegerfield(L, HTTP_INTERNAL_SERVER_ERROR);
200       makeintegerfield(L, HTTP_NOT_IMPLEMENTED);
201       makeintegerfield(L, HTTP_BAD_GATEWAY);
202       makeintegerfield(L, HTTP_SERVICE_UNAVAILABLE);
203       makeintegerfield(L, HTTP_GATEWAY_TIME_OUT);
204       makeintegerfield(L, HTTP_VERSION_NOT_SUPPORTED);
205       makeintegerfield(L, HTTP_VARIANT_ALSO_VARIES);
206       makeintegerfield(L, HTTP_INSUFFICIENT_STORAGE);
207       makeintegerfield(L, HTTP_LOOP_DETECTED);
208       makeintegerfield(L, HTTP_NOT_EXTENDED);
209       makeintegerfield(L, HTTP_NETWORK_AUTHENTICATION_REQUIRED);
210     */
211}
212
213/* END apache2 lmodule */
214
215/*  END library functions */
216
217/* callback for cleaning up a lua vm when pool is closed */
218static apr_status_t cleanup_lua(void *l)
219{
220    AP_DEBUG_ASSERT(l != NULL);
221    lua_close((lua_State *) l);
222    return APR_SUCCESS;
223}
224
225static apr_status_t server_cleanup_lua(void *resource)
226{
227    ap_lua_server_spec* spec = (ap_lua_server_spec*) resource;
228    AP_DEBUG_ASSERT(spec != NULL);
229    if (spec->L != NULL) {
230        lua_close((lua_State *) spec->L);
231    }
232    return APR_SUCCESS;
233}
234
235/*
236        munge_path(L,
237                   "path",
238                   "?.lua",
239                   "./?.lua",
240                   lifecycle_pool,
241                   spec->package_paths,
242                   spec->file);
243*/
244/**
245 * field -> "path" or "cpath"
246 * sub_pat -> "?.lua"
247 * rep_pat -> "./?.lua"
248 * pool -> lifecycle pool for allocations
249 * paths -> things to add
250 * file -> ???
251 */
252static void munge_path(lua_State *L,
253                       const char *field,
254                       const char *sub_pat,
255                       const char *rep_pat,
256                       apr_pool_t *pool,
257                       apr_array_header_t *paths,
258                       const char *file)
259{
260    const char *current;
261    const char *parent_dir;
262    const char *pattern;
263    const char *modified;
264    char *part;
265
266    lua_getglobal(L, "package");
267    lua_getfield(L, -1, field);
268
269    current = lua_tostring(L, -1);
270
271    parent_dir = ap_make_dirstr_parent(pool, file);
272
273    pattern = apr_pstrcat(pool, parent_dir, sub_pat, NULL);
274
275    luaL_gsub(L, current, rep_pat, pattern);
276    lua_setfield(L, -3, field);
277    lua_getfield(L, -2, field);
278    modified = lua_tostring(L, -1);
279
280
281    lua_pop(L, 2);
282
283    part = apr_pstrcat(pool, modified, ";", apr_array_pstrcat(pool, paths, ';'),
284                       NULL);
285
286    lua_pushstring(L, part);
287    lua_setfield(L, -2, field);
288    lua_pop(L, 1);              /* pop "package" off the stack     */
289}
290
291#ifdef AP_ENABLE_LUAJIT
292static int loadjitmodule(lua_State *L, apr_pool_t *lifecycle_pool) {
293    lua_getglobal(L, "require");
294    lua_pushliteral(L, "jit.");
295    lua_pushvalue(L, -3);
296    lua_concat(L, 2);
297    if (lua_pcall(L, 1, 1, 0)) {
298        const char *msg = lua_tostring(L, -1);
299        ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, lifecycle_pool, APLOGNO(01480)
300                      "Failed to init LuaJIT: %s", msg);
301        return 1;
302    }
303    lua_getfield(L, -1, "start");
304    lua_remove(L, -2);  /* drop module table */
305    return 0;
306}
307
308#endif
309
310static apr_status_t vm_construct(lua_State **vm, void *params, apr_pool_t *lifecycle_pool)
311{
312    lua_State* L;
313
314    ap_lua_vm_spec *spec = params;
315
316    L = luaL_newstate();
317#ifdef AP_ENABLE_LUAJIT
318    luaopen_jit(L);
319#endif
320    luaL_openlibs(L);
321    if (spec->package_paths) {
322        munge_path(L,
323                   "path", "?.lua", "./?.lua",
324                   lifecycle_pool,
325                   spec->package_paths,
326                   spec->file);
327    }
328    if (spec->package_cpaths) {
329        munge_path(L,
330                   "cpath", "?" AP_LUA_MODULE_EXT, "./?" AP_LUA_MODULE_EXT,
331                   lifecycle_pool,
332                   spec->package_cpaths,
333                   spec->file);
334    }
335
336    if (spec->cb) {
337        spec->cb(L, lifecycle_pool, spec->cb_arg);
338    }
339
340
341    if (spec->bytecode && spec->bytecode_len > 0) {
342        luaL_loadbuffer(L, spec->bytecode, spec->bytecode_len, spec->file);
343        lua_pcall(L, 0, LUA_MULTRET, 0);
344    }
345    else {
346        int rc;
347        ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, lifecycle_pool, APLOGNO(01481)
348            "loading lua file %s", spec->file);
349        rc = luaL_loadfile(L, spec->file);
350        if (rc != 0) {
351            ap_log_perror(APLOG_MARK, APLOG_ERR, 0, lifecycle_pool, APLOGNO(01482)
352                          "Error loading %s: %s", spec->file,
353                          rc == LUA_ERRMEM ? "memory allocation error"
354                                           : lua_tostring(L, 0));
355            return APR_EBADF;
356        }
357        lua_pcall(L, 0, LUA_MULTRET, 0);
358    }
359
360#ifdef AP_ENABLE_LUAJIT
361    loadjitmodule(L, lifecycle_pool);
362#endif
363    lua_pushlightuserdata(L, lifecycle_pool);
364    lua_setfield(L, LUA_REGISTRYINDEX, "Apache2.Wombat.pool");
365    *vm = L;
366
367    return APR_SUCCESS;
368}
369
370static ap_lua_vm_spec* copy_vm_spec(apr_pool_t* pool, ap_lua_vm_spec* spec)
371{
372    ap_lua_vm_spec* copied_spec = apr_pcalloc(pool, sizeof(ap_lua_vm_spec));
373    copied_spec->bytecode_len = spec->bytecode_len;
374    copied_spec->bytecode = apr_pstrdup(pool, spec->bytecode);
375    copied_spec->cb = spec->cb;
376    copied_spec->cb_arg = NULL;
377    copied_spec->file = apr_pstrdup(pool, spec->file);
378    copied_spec->package_cpaths = apr_array_copy(pool, spec->package_cpaths);
379    copied_spec->package_paths = apr_array_copy(pool, spec->package_paths);
380    copied_spec->pool = pool;
381    copied_spec->scope = AP_LUA_SCOPE_SERVER;
382    copied_spec->codecache = spec->codecache;
383    return copied_spec;
384}
385
386static apr_status_t server_vm_construct(lua_State **resource, void *params, apr_pool_t *pool)
387{
388    lua_State* L;
389    ap_lua_server_spec* spec = apr_pcalloc(pool, sizeof(ap_lua_server_spec));
390    *resource = NULL;
391    if (vm_construct(&L, params, pool) == APR_SUCCESS) {
392        spec->finfo = apr_pcalloc(pool, sizeof(ap_lua_finfo));
393        if (L != NULL) {
394            spec->L = L;
395            *resource = (void*) spec;
396            lua_pushlightuserdata(L, spec);
397            lua_setfield(L, LUA_REGISTRYINDEX, "Apache2.Lua.server_spec");
398            return APR_SUCCESS;
399        }
400    }
401    return APR_EGENERAL;
402}
403
404/**
405 * Function used to create a lua_State instance bound into the web
406 * server in the appropriate scope.
407 */
408lua_State *ap_lua_get_lua_state(apr_pool_t *lifecycle_pool,
409                                               ap_lua_vm_spec *spec, request_rec* r)
410{
411    lua_State *L = NULL;
412    ap_lua_finfo *cache_info = NULL;
413    int tryCache = 0;
414
415    if (spec->scope == AP_LUA_SCOPE_SERVER) {
416        char *hash;
417        apr_reslist_t* reslist = NULL;
418        ap_lua_server_spec* sspec = NULL;
419        hash = apr_psprintf(r->pool, "reslist:%s", spec->file);
420#if APR_HAS_THREADS
421        apr_thread_mutex_lock(ap_lua_mutex);
422#endif
423        if (apr_pool_userdata_get((void **)&reslist, hash,
424                                  r->server->process->pool) == APR_SUCCESS) {
425            if (reslist != NULL) {
426                if (apr_reslist_acquire(reslist, (void**) &sspec) == APR_SUCCESS) {
427                    L = sspec->L;
428                    cache_info = sspec->finfo;
429                }
430            }
431        }
432        if (L == NULL) {
433            ap_lua_vm_spec* server_spec = copy_vm_spec(r->server->process->pool, spec);
434            if (
435                    apr_reslist_create(&reslist, spec->vm_min, spec->vm_max, spec->vm_max, 0,
436                                (apr_reslist_constructor) server_vm_construct,
437                                (apr_reslist_destructor) server_cleanup_lua,
438                                server_spec, r->server->process->pool)
439                    == APR_SUCCESS && reslist != NULL) {
440                apr_pool_userdata_set(reslist, hash, NULL,
441                                            r->server->process->pool);
442                if (apr_reslist_acquire(reslist, (void**) &sspec) == APR_SUCCESS) {
443                    L = sspec->L;
444                    cache_info = sspec->finfo;
445                }
446                else {
447                    return NULL;
448                }
449            }
450        }
451#if APR_HAS_THREADS
452        apr_thread_mutex_unlock(ap_lua_mutex);
453#endif
454    }
455    else {
456        if (apr_pool_userdata_get((void **)&L, spec->file,
457                              lifecycle_pool) != APR_SUCCESS) {
458            L = NULL;
459        }
460    }
461    if (L == NULL) {
462        ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, lifecycle_pool, APLOGNO(01483)
463                        "creating lua_State with file %s", spec->file);
464        /* not available, so create */
465
466        if (!vm_construct(&L, spec, lifecycle_pool)) {
467            AP_DEBUG_ASSERT(L != NULL);
468            apr_pool_userdata_set(L, spec->file, cleanup_lua, lifecycle_pool);
469        }
470    }
471
472    if (spec->codecache == AP_LUA_CACHE_FOREVER || (spec->bytecode && spec->bytecode_len > 0)) {
473        tryCache = 1;
474    }
475    else {
476        char* mkey;
477        if (spec->scope != AP_LUA_SCOPE_SERVER) {
478            mkey = apr_psprintf(r->pool, "ap_lua_modified:%s", spec->file);
479            apr_pool_userdata_get((void **)&cache_info, mkey, lifecycle_pool);
480            if (cache_info == NULL) {
481                cache_info = apr_pcalloc(lifecycle_pool, sizeof(ap_lua_finfo));
482                apr_pool_userdata_set((void*) cache_info, mkey, NULL, lifecycle_pool);
483            }
484        }
485        if (spec->codecache == AP_LUA_CACHE_STAT) {
486            apr_finfo_t lua_finfo;
487            apr_stat(&lua_finfo, spec->file, APR_FINFO_MTIME|APR_FINFO_SIZE, lifecycle_pool);
488
489            /* On first visit, modified will be zero, but that's fine - The file is
490            loaded in the vm_construct function.
491            */
492            if ((cache_info->modified == lua_finfo.mtime && cache_info->size == lua_finfo.size)
493                    || cache_info->modified == 0) {
494                tryCache = 1;
495            }
496            cache_info->modified = lua_finfo.mtime;
497            cache_info->size = lua_finfo.size;
498        }
499        else if (spec->codecache == AP_LUA_CACHE_NEVER) {
500            if (cache_info->runs == 0)
501                tryCache = 1;
502        }
503        cache_info->runs++;
504    }
505    if (tryCache == 0 && spec->scope != AP_LUA_SCOPE_ONCE) {
506        int rc;
507        ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, lifecycle_pool, APLOGNO(02332)
508            "(re)loading lua file %s", spec->file);
509        rc = luaL_loadfile(L, spec->file);
510        if (rc != 0) {
511            ap_log_perror(APLOG_MARK, APLOG_ERR, 0, lifecycle_pool, APLOGNO(02333)
512                          "Error loading %s: %s", spec->file,
513                          rc == LUA_ERRMEM ? "memory allocation error"
514                                           : lua_tostring(L, 0));
515            return 0;
516        }
517        lua_pcall(L, 0, LUA_MULTRET, 0);
518    }
519
520    return L;
521}
522