build: split into luci and luci-addons packages
[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 }