fix fastindex namespace
[project/luci.git] / libs / fastindex / src / fastindex.c
1 /*
2  * fastindex - fast lua module indexing plugin
3  * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <sys/types.h>
16 #include <sys/time.h>
17 #include <sys/cdefs.h>
18
19 #ifndef _POSIX_C_SOURCE
20 #define _POSIX_C_SOURCE /* XXX: portability hack for timestamp */
21 #endif
22
23 #include <sys/stat.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <glob.h>
30
31 #include <lualib.h>
32 #include <lauxlib.h>
33 #include "list.h"
34
35 #define MODNAME        "luci.fastindex"
36 #define DEFAULT_BUFLEN 1024
37
38 //#define DEBUG 1
39
40 #ifdef DEBUG
41 #define DPRINTF(...) fprintf(stderr, __VA_ARGS__)
42 #else
43 #define DPRINTF(...) do {} while (0)
44 #endif
45
46 /**
47  * list_for_each_offset -       iterate over a list, start with the provided pointer
48  * @pos:        the &struct list_head to use as a loop cursor.
49  * @head:       the head for your list.
50  */
51 #define list_for_each_offset(pos, head, offset) \
52         for (pos = (offset)->next; pos != (offset); \
53              pos = ((pos->next == (head)) && ((offset) != (head)) ? (head)->next : pos->next))
54
55 static char *namespace = NULL;
56
57 struct fastindex_entry {
58         struct list_head list;
59         time_t timestamp;
60         int checked;
61         char *name;
62 };
63
64 struct fastindex_pattern {
65         struct list_head list;
66         char pattern[];
67 };
68
69 struct fastindex {
70         lua_State *L;
71         int checked;
72         char *func;
73         struct list_head patterns;
74         struct list_head *last;
75         struct list_head entries;
76         int ofs;
77         char *buf;
78         int buflen;
79 };
80
81 static inline struct fastindex *
82 to_fastindex(struct lua_State *L)
83 {
84         struct fastindex *f;
85         lua_getfield(L, lua_upvalueindex(1), "__data");
86         f = lua_touserdata(L, -1);
87         lua_pop(L, 1);
88         return f;
89 }
90
91 static int
92 fastindex_module(lua_State *L)
93 {
94         const char *s;
95         s = luaL_checkstring(L, 1);
96
97         if (s) {
98                 if (namespace)
99                         free(namespace);
100                 namespace = strdup(s);
101         }
102
103         return 0;
104 }
105
106 static struct fastindex_entry *
107 find_entry(struct fastindex *f, char *name)
108 {
109         struct list_head *p;
110
111         if (!f->last)
112                 f->last = &f->entries;
113
114         list_for_each_offset(p, &f->entries, f->last) {
115                 struct fastindex_entry *e;
116                 e = container_of(p, struct fastindex_entry, list);
117                 if (!strcmp(e->name, name))
118                         return e;
119         }
120         return NULL;
121 }
122
123 static struct fastindex_entry *
124 new_entry(struct fastindex *f, char *name)
125 {
126         struct fastindex_entry *e;
127
128         e = malloc(sizeof(struct fastindex_entry));
129         if (!e)
130                 goto error;
131
132         memset(e, 0, sizeof(struct fastindex_entry));
133         e->name = strdup(name);
134         if (!e->name) {
135                 free(e);
136                 goto error;
137         }
138         INIT_LIST_HEAD(&e->list);
139
140         return e;
141
142 error:
143         return NULL;
144 }
145
146 static void free_entry(struct fastindex_entry *e)
147 {
148         list_del(&e->list);
149         free(e->name);
150         free(e);
151 }
152
153 int bufferwriter(lua_State *L, const void *p, size_t sz, void *ud)
154 {
155         struct fastindex *f = ud;
156
157         while (f->ofs + sz > f->buflen) {
158                 char *b = f->buf;
159                 f->buflen *= 2;
160                 f->buf = realloc(f->buf, f->buflen);
161                 if (!f->buf) {
162                         free(b);
163                         return 1;
164                 }
165         }
166         memcpy(f->buf + f->ofs, p, sz);
167         f->ofs += sz;
168         return 0;
169 }
170
171 static void
172 load_index(struct fastindex *f, struct fastindex_entry *e)
173 {
174         lua_State *L;
175
176         DPRINTF("Loading module: %s\n", e->name);
177
178         if (!f->buf)
179                 f->buf = malloc(f->buflen);
180
181         if (!f->buf)
182                 luaL_error(f->L, "Out of memory!\n");
183
184         f->ofs = 0;
185         L = luaL_newstate();
186         if (!L)
187                 return;
188
189         namespace = NULL;
190         luaL_openlibs(L);
191         lua_pushcfunction(L, fastindex_module);
192         lua_setfield(L, LUA_GLOBALSINDEX, "module");
193
194         do {
195                 if (luaL_dofile(L, e->name)) {
196                         DPRINTF("Warning: unable to open module '%s'\n", e->name);
197                         break;
198                 }
199
200                 lua_getglobal(L, f->func);
201                 lua_dump(L, bufferwriter, f);
202                 DPRINTF("Got %d bytes\n", f->ofs);
203                 if (f->ofs == 0)
204                         break;
205                 lua_createtable(f->L, (namespace ? 2 : 1), 0);
206                 luaL_loadbuffer(f->L, f->buf, f->ofs, "tmp");
207                 lua_rawseti(f->L, -2, 1);
208                 if (namespace) {
209                         DPRINTF("Module has namespace '%s'\n", namespace);
210                         lua_pushstring(f->L, namespace);
211                         lua_rawseti(f->L, -2, 2);
212                         free(namespace);
213                         namespace = NULL;
214                 }
215                 lua_setfield(f->L, -2, e->name);
216         } while (0);
217
218         lua_close(L);
219 }
220
221
222 static int
223 fastindex_scan(lua_State *L)
224 {
225         struct list_head *tmp, *p;
226         struct fastindex *f;
227         glob_t gl;
228         int i;
229         int gl_flags = GLOB_NOESCAPE | GLOB_NOSORT | GLOB_MARK;
230
231         f = to_fastindex(L);
232         f->checked++;
233
234
235         if (list_empty(&f->patterns))
236                 return 0;
237
238         lua_getfield(L, lua_upvalueindex(1), "indexes");
239         list_for_each(p, &f->patterns) {
240                 struct fastindex_pattern *pt = container_of(p, struct fastindex_pattern, list);
241                 glob(pt->pattern, gl_flags, NULL, &gl);
242                 gl_flags |= GLOB_APPEND;
243         }
244         for (i = 0; i < gl.gl_pathc; i++) {
245                 struct fastindex_entry *e;
246                 struct stat st;
247
248                 if (stat(gl.gl_pathv[i], &st))
249                         continue;
250
251                 if ((st.st_mode & S_IFMT) != S_IFREG)
252                         continue;
253
254                 e = find_entry(f, gl.gl_pathv[i]);
255                 if (!e) {
256                         e = new_entry(f, gl.gl_pathv[i]);
257                         list_add_tail(&e->list, &f->entries);
258                 }
259
260                 e->checked = f->checked;
261                 if ((e->timestamp < st.st_mtime)) {
262                         load_index(f, e);
263                         e->timestamp = st.st_mtime;
264                 }
265         }
266         globfree(&gl);
267         list_for_each_safe(p, tmp, &f->entries) {
268                 struct fastindex_entry *e = container_of(p, struct fastindex_entry, list);
269                 if (e->checked < f->checked) {
270                         lua_pushnil(f->L);
271                         lua_setfield(f->L, -2, e->name);
272                         free_entry(e);
273                 }
274         }
275         lua_pop(L, 1);
276
277         return 0;
278 }
279
280 static int
281 fastindex_free(lua_State *L)
282 {
283         struct fastindex *f;
284         struct list_head *p, *tmp;
285
286         f = lua_touserdata(L, -1);
287         list_for_each_safe(p, tmp, &f->patterns) {
288                 struct fastindex_pattern *pt;
289                 pt = container_of(p, struct fastindex_pattern, list);
290                 list_del(p);
291                 free(pt);
292         }
293         list_for_each_safe(p, tmp, &f->entries) {
294                 struct fastindex_entry *e;
295                 e = container_of(p, struct fastindex_entry, list);
296                 free_entry(e);
297         }
298         return 0;
299 }
300
301 static int
302 fastindex_add(lua_State *L)
303 {
304         struct fastindex_pattern *pt;
305         struct fastindex *f;
306         const char *str;
307
308         f = to_fastindex(L);
309         str = luaL_checkstring(L, 1);
310         if (!str)
311                 luaL_error(L, "Invalid argument");
312
313         pt = malloc(sizeof(struct fastindex_pattern) + strlen(str) + 1);
314         if (!pt)
315                 luaL_error(L, "Out of memory");
316
317         INIT_LIST_HEAD(&pt->list);
318         strcpy(pt->pattern, str);
319         list_add(&pt->list, &f->patterns);
320
321         return 0;
322 }
323
324 static const luaL_Reg fastindex_m[] = {
325         { "add", fastindex_add },
326         { "scan", fastindex_scan },
327         { NULL, NULL }
328 };
329
330 static int
331 fastindex_new(lua_State *L)
332 {
333         struct fastindex *f;
334         const char *func;
335
336         func = luaL_checkstring(L, 1);
337
338         f = lua_newuserdata(L, sizeof(struct fastindex));
339         lua_createtable(L, 0, 2);
340         lua_pushvalue(L, -1);
341         lua_setfield(L, -2, "__index");
342         lua_pushcfunction(L, fastindex_free);
343         lua_setfield(L, -2, "__gc");
344         lua_pushvalue(L, -1);
345         lua_setmetatable(L, -3);
346         lua_pushvalue(L, -2);
347         lua_setfield(L, -2, "__data");
348         lua_createtable(L, 0, 1);
349         lua_setfield(L, -2, "indexes");
350         lua_pushvalue(L, -2);
351         luaI_openlib(L, NULL, fastindex_m, 1);
352
353         memset(f, 0, sizeof(struct fastindex));
354         f->L = L;
355         f->buflen = DEFAULT_BUFLEN;
356         INIT_LIST_HEAD(&f->entries);
357         INIT_LIST_HEAD(&f->patterns);
358
359         f->func = strdup(func);
360         if (!f->func) {
361                 if (f->func)
362                         free(f->func);
363                 luaL_error(L, "Out of memory\n");
364         }
365
366         return 1;
367 }
368
369 static const luaL_Reg fastindex[] = {
370         { "new", fastindex_new },
371         { NULL, NULL },
372 };
373
374 int
375 luaopen_luci_fastindex(lua_State *L)
376 {
377         luaL_register(L, MODNAME, fastindex);
378         return 0;
379 }